2017-3-26四校联考

丧病出题人 我比较菜 只写了T1 100/400

T1.约会

题目大意:一棵树,每个点i有权值wi,支持三种操作:1.给定x,询问随机选出i!=j且lca(i,j)=x,wi+wj的期望;2.使一个点的权值加上k;3.使一棵子树内所有点的权值加上k。(n,操作数<=300,000)

思路:对于一个lca(i,j)=x,选出的i和j必然在不同的子树内或者就在x,则

$$ ans[x]=frac{2*(sum (sum[son[x]]*(size[x]-size[son[x]])) +w[x]*(size[x]-1))}{sum (size[son[x]]*(size[x]-size[son[x]]))+size[x]-1} $$

,先预处理出每个点的答案的式子的前半及后半的值,修改单点点权,会使该点及其祖先的答案发生更改,自己的答案把前半式子展开后发现可以O(1)修改,对祖先的修改我们先进行树剖,从一条重链跳到另一条时直接O(1)修改,若在重链上,用mx[x]表示x的重链连向的儿子,则重链上的点的前半式子会被修改k*(size[x]-size[mx[x]]),用线段树维护区间加k,处理询问使再乘上(size[x]-size[mx[x]])即可;再考虑子树加,显然子树内的所有点答案都会加上2k,而这次修改对祖先的贡献的计算方法与单点修改同理。总复杂度O(nlogn^2)(常数较小)。

#include<cstdio>
#define ll long long
inline int read()
{
    int x,f=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=0;
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return f?x:-x;
}
#define MN 300000
#define N 524288
struct edge{int nx,t;}e[MN+5];
int h[MN+5],en,l[MN+5],r[MN+5],cnt,f[MN+5],fa[MN+5],mx[MN+5],s[MN+5];
long double a[MN+5];
ll w[MN+5],b[MN+5],t1[N*2+5],t2[N*2+5];
inline void ins(int x,int y){e[++en]=(edge){h[x],y};h[x]=en;}
void dfs(int x)
{
    s[x]=1;a[x]=-w[x];
    for(int i=h[x];i;i=e[i].nx)
    {
        fa[e[i].t]=x;dfs(e[i].t);
        s[x]+=s[e[i].t];w[x]+=w[e[i].t];
        if(s[e[i].t]>s[mx[x]])mx[x]=e[i].t;
        a[x]-=w[e[i].t]*s[e[i].t];
        b[x]-=1LL*s[e[i].t]*s[e[i].t];
    }
    a[x]+=w[x]*s[x];
    b[x]+=1LL*s[x]*s[x]-1;
}
void build(int x,int k)
{
    l[x]=++cnt;f[x]=k;
    if(mx[x])build(mx[x],k);
    for(int i=h[x];i;i=e[i].nx)if(e[i].t!=mx[x])build(e[i].t,e[i].t);
    r[x]=cnt;
}
void add(ll*t,int l,int r,ll x)
{
    for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1)t[l+1]+=x;
        if( r&1)t[r-1]+=x;
    }
}
ll query(ll*t,int k){ll r=0;for(k+=N;k;k>>=1)r+=t[k];return r;}
void up(int x,ll z)
{
    for(;;)
    {
        if(x!=f[x])add(t2,l[f[x]],l[x]-1,z);
        if(!fa[f[x]])return;
        a[fa[f[x]]]+=(s[fa[f[x]]]-s[f[x]])*z;
        x=fa[f[x]];
    }
}
int main()
{
    freopen("meet.in","r",stdin);
    freopen("meet.out","w",stdout);
    int n,m,i,x;char t[5];
    n=read();m=read();
    for(i=2;i<=n;++i)ins(read(),i);
    for(i=1;i<=n;++i)w[i]=read();
    dfs(1);build(1,1);
    while(m--)
    {
        scanf("%s",t);i=read();
        if(t[0]=='Q')printf("%.6lf
",
        (double)(s[i]>1?2*(query(t1,l[i])+(a[i]+(long double)query(t2,l[i])*(s[i]-s[mx[i]]))/b[i]):0));
        if(t[0]=='S')a[i]+=1LL*(s[i]-1)*(x=read()),up(i,x);
        if(t[0]=='M')add(t1,l[i],r[i],x=read()),up(i,1LL*(r[i]-l[i]+1)*x);
    }
    fclose(stdin);fclose(stdout);return 0;
}

T3.山脉

题目大意:一条山脉由n个点组成,每个点有一个高度,高度大于左右两个点的称作山峰,每次把一个区间内的高度全部加上一个公差为正的等差数列,并询问此时有多少座山峰。(n,操作数<=100,000,出现的数字的绝对值<=100,000)

思路:显然先把原数组差分,问题转化为每次修改两个点,区间加正数,询问有多少相邻的数字一个大于0一个小于0,先算一遍答案,修改两个点我们可以直接暴力,区间加显然只会当负数变为0或正数、0变成正数时会使答案变化,由于一开始负数/0最多n个,之后每次操作最多使负数/0增多2个,所以这个变化发生的次数最多是O(n)的,我们用线段树维护区间中最大的负数/0,如果区间加后区间内最大的负数非负了或者0超过0了,我们直接修改他,重复这个过程,总复杂度O(nlogn)。

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
inline int read()
{
    int x,f=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=0;
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return f?x:-x;
}
#define MN 100000
#define N 131072
#define INF (1LL<<60)
#define L (k<<1)
#define R (k<<1|1)
ll a[MN+5];int ans[N*2+5];
void change(int k,int x){for(ans[k+=N]=x;k>>=1;)ans[k]=ans[L]+ans[R];}
struct data
{
    ll mf,m0;int pf,p0;
    data(ll mf=0,int pf=0,ll m0=0,int p0=0):mf(mf),pf(pf),m0(m0),p0(p0){}
    data(int x){pf=p0=x;mf=a[x]<0?a[x]:-INF;m0=a[x]?-INF:0;}
    inline friend data operator+(data a,data b)
    {
        return data(max(a.mf,b.mf),a.mf>b.mf?a.pf:b.pf,
                    max(a.m0,b.m0),a.m0>b.m0?a.p0:b.p0);
    }
    inline void operator+=(ll x){mf+=x;m0+=x;}
};
struct node{int l,r;ll mk;data x;}t[MN*4+5];
inline void up(int k){t[k].x=t[L].x+t[R].x;}
inline void add(int k,ll x){t[k].x+=x;t[k].mk+=x;}
inline void down(int k){if(t[k].mk)add(L,t[k].mk),add(R,t[k].mk),t[k].mk=0;}
void build(int k,int l,int r)
{
    if((t[k].l=l)==(t[k].r=r)){t[k].x=data(l);return;}
    int mid=l+r>>1;
    build(L,l,mid);build(R,mid+1,r);up(k);
}
ll query(int k,int x)
{
    while(t[k].l<t[k].r)down(k),k=x>t[k].l+t[k].r>>1?R:L;
    return a[t[k].l]+t[k].mk;
}
void add(int k,int x,ll ad)
{
    while(t[k].l<t[k].r)down(k),k=x>t[k].l+t[k].r>>1?R:L;
    a[t[k].l]+=t[k].mk+ad;t[k].mk=0;
    change(x,query(1,x-1)>0&&a[t[k].l]<0);
    change(x+1,a[t[k].l]>0&&query(1,x+1)<0);
    t[k].x=data(t[k].l);
    while(k>>=1)up(k);
}
void add(int k,int l,int r,int ad)
{
    if(t[k].l==l&&t[k].r==r){add(k,ad);return;}
    int mid=t[k].l+t[k].r>>1;down(k);
    if(r<=mid)add(L,l,r,ad);
    else if(l>mid)add(R,l,r,ad);
    else add(L,l,mid,ad),add(R,mid+1,r,ad);
    up(k);
}
data query(int k,int l,int r)
{
    if(t[k].l==l&&t[k].r==r)return t[k].x;
    int mid=t[k].l+t[k].r>>1;down(k);
    if(r<=mid)return query(L,l,r);
    if(l>mid)return query(R,l,r);
    return query(L,l,mid)+query(R,mid+1,r);
}
int main()
{
    int n,m,i,l,r,a1,d;data x;
    n=read();m=read();
    for(i=1;i<=n;++i)a[i]=read();
    for(i=1;i<n;++i)a[i]=a[i+1]-a[i];a[n]=0;
    for(i=2;i<n;++i)if(a[i-1]>0&&a[i]<0)change(i,1);
    build(1,0,n);
    printf("%d
",ans[1]);
    while(m--)
    {
        l=read();r=read();a1=read();d=read();
        if(l>1)add(1,l-1,a1);
        if(l<r)for(add(1,l,r-1,d);;)
            if((x=query(1,l,r)).mf>=0)add(1,x.pf,0);
            else if(x.m0>0)add(1,x.p0,0);
            else break;
        if(r<n)add(1,r,-a1-1LL*(r-l)*d);
        printf("%d
",ans[1]);
    }
}
原文地址:https://www.cnblogs.com/ditoly/p/20170326C.html