关闭

树上操作 解题报告

标签: BIT差分前缀和dfs序
529人阅读 评论(0) 收藏 举报
分类:

两种操作:
1.输入X,s,t,对于链s-t,设上面的点为v1,..,k,给vi加上iXRi
2.输入s,t,询问链s-t的权值和,对100711433取模。
所有操作1的R相同,在一开始会给出。
所以操作1在操作2之后出现,操作1有U个,操作2有Q个。
1n,Q,U105,1R,X109
时限1.5s,空间限制256MB。

想出标算的时候就剩1个半小时了,然后自不量力开始码标算,然后开始各种推错式子,最后还是交了暴力,结果暴力爆零了。
原因是因为暴力用dfs序对付了询问,结果bit的时候把n<<1写成了1<<n;然后试了几组小数据没有问题,大数据就吃翔了。

标算的话。。既然修改和询问是分开的,也就是说如果我们能先把每个点的点权搞出来,然后就可以直接上dfs序+bit了。
那么问题在于修改的贡献该如何累积,注意这个式子XiRi,它本身虽然不太好求,但它的前缀我们是回求的,那我们求一下前缀然后差分一下就好了。

Sn=i=1nXiRi
,则
RSn=i=1nXiRi+1
,
(1R)Sn=i=1nXRiXRn+1=XR(Rn1)R1nRn+1
(R1)Sn=XnRn+1XRn+1R1XRR1
Sn=RR1(XnRn1R1XRnXRR1)

然后显然可以就可以维护XiaiRaiXiRai,我一开始想的是维护当前的和前一个的然后求点权的时候再减,但后来发现(写起来太吃屎了)其实反正都有什么分配律什么的,直接维护差分后的就行。
因为这是区间修改、单点查询,所以我们考虑差分序列。搞出原树的重链,然后把每一个修改对于每一条链打一个载入标记和一个载出标记;注意到因为一个修改需要先上去再下去走两遍,所以要打两遍标记。因为一条链只会跨过log2n的重链,所以这样做的时间复杂度是O(nlog2n)的。
打完标记以后就是说我们需要面对这样一个事情,知道当前的XiaiRaiXiRai,怎么求Xi(ai+1)Rai+1XiRai+1?显然,XiRai+1=RXiRai,Xi(ai+1)Rai+1=RXiaiRai+XiaiRai+1,这样我们就以搞了。
但是还有一个问题,注意到分母上出现了R-1,那么就要求R-1有逆元,可如果R-1没有逆元怎么办?100711433是个质数,所以R-1没有逆元,当且仅当R1=0(mod 100711433),也就是R=1(mod 100711433),所以我们需要单独处理这种蛋疼的情况,显然这时
Sn=Xn(n+1)2=X12(n2+n)
,所以基本一样,只要维护Xia2iXiai即可,然后因为差分了,所以Xiai其实不会变,Xi(ai+1)2=Xia2i+2Xiai后面的1差分掉了。

然后。。总时间复杂度O(nlog2n)思路还算尚可,代码就直接吃翔了。
代码(暴力):

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
using namespace std;
#define Mod 100711433
char * cp=(char *)malloc(10000000);
inline void in(int &x){
    while(*cp<'0'||*cp>'9')++cp;
    x=0;
    while(*cp>='0'&&*cp<='9')x=x*10+(*cp++^'0');
}
int ptr[100005],succ[200005],next[200005],etot=1;
inline void addedge(int u,int v){
    next[etot]=ptr[u],ptr[u]=etot,succ[etot++]=v;
}
int fa[100005][18],depth[100005],bl[100005],br[100005],btot=1;
void dfs(int node){
    depth[node]=depth[fa[node][0]]+1;
    bl[node]=btot++;
    for(int i=ptr[node];i;i=next[i])
        if(succ[i]!=fa[node][0]){
            fa[succ[i]][0]=node;
            for(int j=1;j<18;++j)fa[succ[i]][j]=fa[fa[succ[i]][j-1]][j-1];
            dfs(succ[i]);
        }
    br[node]=btot++;
}
int path[100005];
typedef long long LL;
LL fac[100005];
int p[100005];
int n;
int bit[200005];
void add(int x,int A){
    for(;x<=1<<n;x+=x&-x)bit[x]=(bit[x]+A)%Mod;
}
int query(int x){
    int ans=0;
    for(;x;x-=x&-x)ans=(ans+bit[x])%Mod;
    return ans;
}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree_50.out","w",stdout);
    fread(cp,1,10000000,stdin);
    int U,Q,R,i;
    in(n),in(R);
    fac[0]=1;
    for(i=1;i<=n;++i)fac[i]=fac[i-1]*R%Mod;
    int u,v;
    for(i=n;--i;){
        in(u),in(v);
        addedge(u,v),addedge(v,u);
    }
    in(U),in(Q);
    int root=(rand()<<16|rand())%n+1;
    dfs(root);
    int s,t,ptot,X;
    while(U--){
        in(X),in(s),in(t);
        u=s,v=t;
        ptot=0;
        while(u!=v)
            if(depth[u]>depth[v]){
                path[ptot++]=u;
                u=fa[u][0];
            }
            else v=fa[v][0];
        path[ptot++]=u;
        v=t;
        ptot+=depth[v]-depth[u];
        for(;u!=v;v=fa[v][0])path[--ptot]=v;
        ptot+=depth[t]-depth[u];
        for(i=0;i<ptot;++i)p[path[i]]=(p[path[i]]+X*fac[i+1]%Mod*(i+1)%Mod)%Mod;
    }
    for(i=n;i;--i)add(bl[i],p[i]),add(br[i],-p[i]);
    int ans;
    while(Q--){
        in(s),in(t);
        ans=(query(bl[s])+query(bl[t]))%Mod;
        if(depth[s]>depth[t])swap(s,t);
        for(i=18;i--;)
            if(1<<i&depth[t]-depth[s])
                t=fa[t][i];
        for(i=18;i--;)
            if(fa[s][i]!=fa[t][i])
                s=fa[s][i],t=fa[t][i];
        if(s!=t)s=fa[s][0];
        ans=((ans-(query(bl[s])+query(bl[fa[s][0]]))%Mod)%Mod+Mod)%Mod;
        printf("%d\n",ans);
    }
}

代码(标算):

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
#include<cstdlib>
#define Mod 100711433
char * cp=(char *)malloc(10000000);
int ptr[100005],succ[200005],next[200005],etot=1;
inline void in(int &x){
    while(*cp<'0'||*cp>'9')++cp;
    x=0;
    while(*cp>='0'&&*cp<='9')x=x*10+(*cp++^'0');
}
inline void addedge(int u,int v){
    next[etot]=ptr[u],ptr[u]=etot,succ[etot++]=v;
}
typedef long long LL;
inline int pow(int a,int b){
    LL ans=1,t=a;
    for(;b;b>>=1,t=t*t%Mod)
        if(b&1)
            ans=ans*t%Mod;
    return ans;
}
LL sr[100005],sir[100005];//R≠1
LL sum2[100005],sum1[100005];
int fa[100005],size[100005],depth[100005],maxson[100005],top[100005];
void sizedfs(int node){
    depth[node]=depth[fa[node]]+1;
    size[node]=1;
    for(int i=ptr[node];i;i=next[i])
        if(succ[i]!=fa[node]){
            fa[succ[i]]=node;
            sizedfs(succ[i]);
            size[node]+=size[succ[i]];
        }
}
int bl[100005],br[100005],btot=1;
LL bit[200005];
void builddfs(int node,int wt){
    top[node]=wt;
    bl[node]=btot++;
    for(int i=ptr[node];i;i=next[i])
        if(succ[i]!=fa[node]&&size[succ[i]]>size[maxson[node]])
            maxson[node]=succ[i];
    if(maxson[node]){
        builddfs(maxson[node],wt);
        for(int i=ptr[node];i;i=next[i])
            if(succ[i]!=maxson[node]&&succ[i]!=fa[node])
                builddfs(succ[i],succ[i]);
    }
    br[node]=btot++;
}
int p[100005],s[100005],t[100005],X[100005],lca[100005];
LL fac[100005];
inline LL multi(int x){
    return (LL)x*x%Mod;
}
LL nowsr,nowsir;//R≠1
LL nowsum2,nowsum1;
int niyuan;
inline void add1(int node,int dis,int X){
    sum2[node]=(sum2[node]+(multi(dis)-multi(dis-1))%Mod*X%Mod)%Mod;
    sum1[node]=(sum1[node]+X)%Mod;
    //cout<<"Add:"<<node<<","<<dis<<","<<X<<"->"<<sum2[node]<<" "<<sum1[node]<<endl;
}
inline void minus1(int node,int dis,int X){
    sum2[node]=(sum2[node]-(multi(dis)-multi(dis-1))%Mod*X%Mod)%Mod;
    sum1[node]=(sum1[node]-X)%Mod;
    //cout<<"Minus:"<<node<<","<<dis<<","<<X<<"->"<<sum2[node]<<" "<<sum1[node]<<endl;
}
inline void update1(int node){
    nowsum2=(nowsum2+(nowsum1<<1))%Mod;
    nowsum2=(nowsum2+sum2[node])%Mod;
    nowsum1=(nowsum1+sum1[node])%Mod;
    p[node]=(p[node]+(nowsum2+nowsum1)%Mod*niyuan%Mod)%Mod;
    //cout<<"Update:"<<node<<":"<<nowsum2<<","<<nowsum1<<"->"<<p[node]<<endl;
}
inline void add(int node,int dis,int X){
    sum2[node]=(sum2[node]+(dis*fac[dis]%Mod-(dis-1)*fac[dis-1]%Mod)%Mod*X%Mod)%Mod;
    sum1[node]=(sum1[node]+(fac[dis]-fac[dis-1])%Mod*X%Mod)%Mod;
    //cout<<"Add:"<<node<<","<<dis<<","<<X<<"->"<<sum2[node]<<","<<sum1[node]<<" "<<fac[dis]%Mod*X%Mod<<endl;
}
inline void minu(int node,int dis,int X){
    sum2[node]=(sum2[node]-(dis*fac[dis]%Mod-(dis-1)*fac[dis-1]%Mod)%Mod*X%Mod)%Mod;
    sum1[node]=(sum1[node]-(fac[dis]-fac[dis-1])%Mod*X%Mod)%Mod;
    //cout<<"Minus:"<<node<<","<<dis<<","<<X<<"->"<<sum2[node]<<","<<sum1[node]<<" "<<fac[dis]%Mod*X%Mod<<endl;
}
inline void update(int node){
    nowsum1=nowsum1*fac[1]%Mod;
    nowsum2=(nowsum2*fac[1]%Mod+nowsum1)%Mod;
    nowsum2=(nowsum2+sum2[node])%Mod;
    nowsum1=(nowsum1+sum1[node])%Mod;
    p[node]=(p[node]+(nowsum2-nowsum1*niyuan)%Mod*fac[1]%Mod*niyuan)%Mod;
    //cout<<"Update:"<<node<<":"<<nowsum2<<","<<nowsum1<<"->"<<p[node]<<endl;
}
int n;
inline void badd(int x,int A){
    for(;x<=n<<1;x+=x&-x)bit[x]=(bit[x]+A)%Mod;
}
inline int bQ(int x){
    int ans=0;
    for(;x;x-=x&-x)ans=(ans+bit[x])%Mod;
    return ans;
}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    fread(cp,1,10000000,stdin);
    int i,j,u,v,R;
    in(n),in(R);
    R%=Mod;
    fac[0]=1;
    for(i=1;i<=n;++i)fac[i]=fac[i-1]*R%Mod;
    for(i=n;--i;){
        in(u),in(v);
        addedge(u,v),addedge(v,u);
    }
    int root=(rand()<<16|rand())%n+1;
    sizedfs(root);
    builddfs(root,root);
    int U,Q;
    in(U),in(Q);
    //搞出p[i]
    for(i=U;i--;){
        in(X[i]),in(s[i]),in(t[i]);
        u=s[i],v=t[i];
        while(top[u]!=top[v])
            if(depth[top[u]]>depth[top[v]])u=fa[top[u]];
            else v=fa[top[v]];
        if(depth[u]>depth[v])lca[i]=v;
        else lca[i]=u;
    }
    if(R==1)niyuan=pow(2,Mod-2);
    else niyuan=pow(R-1,Mod-2);
    int node;
    if(R==1){
        //上去。(包含lca)
        for(i=U;i--;){
            for(u=s[i];top[u]!=top[lca[i]];u=fa[top[u]])add1(u,depth[s[i]]-depth[u]+1,X[i]);
            add1(u,depth[s[i]]-depth[u]+1,X[i]);
            if(lca[i]!=top[lca[i]])minus1(fa[lca[i]],depth[s[i]]-depth[lca[i]]+2,X[i]);
        }
        for(i=n;i;--i)
            if(maxson[i]==0){
                nowsum2=nowsum1=0;
                for(node=i;top[node]==top[i];node=fa[node])update1(node);
            }
        /*for(i=1;i<=n;++i)cout<<p[i]<<" ";
        cout<<endl;*/
        //cout<<"-------------\n";
        //下来 
        memset(sum2,0,sizeof(sum2));
        memset(sum1,0,sizeof(sum1));
        for(i=U;i--;){
            for(v=t[i];top[v]!=top[lca[i]];v=fa[top[v]]){
                add1(top[v],depth[top[v]]-depth[lca[i]]+depth[s[i]]-depth[lca[i]]+1,X[i]);
                if(maxson[v])minus1(maxson[v],depth[v]-depth[lca[i]]+depth[s[i]]-depth[lca[i]]+2,X[i]);
            }
            if(v!=lca[i]&&maxson[lca[i]]){
                add1(maxson[lca[i]],depth[s[i]]-depth[lca[i]]+2,X[i]);
                if(maxson[v])minus1(maxson[v],depth[s[i]]-depth[lca[i]]+depth[v]-depth[lca[i]]+2,X[i]);
            }
        }
        for(i=n;i;--i)
            if(top[i]==i){
                nowsum2=nowsum1=0;
                for(node=i;node;node=maxson[node])update1(node);
            }
        /*for(i=1;i<=n;++i)cout<<p[i]<<" ";
        cout<<endl;*/
    }
    else{
        //上去。(包含lca)
        for(i=U;i--;){
            for(u=s[i];top[u]!=top[lca[i]];u=fa[top[u]])add(u,depth[s[i]]-depth[u]+1,X[i]);
            add(u,depth[s[i]]-depth[u]+1,X[i]);
            if(top[lca[i]]!=lca[i])minu(fa[lca[i]],depth[s[i]]-depth[lca[i]]+2,X[i]);
        }
        for(i=n;i;--i)
            if(maxson[i]==0){
                nowsum2=nowsum1=0;
                for(node=i;top[node]==top[i];node=fa[node])update(node);
            }
        //下来.不包含lca。 
        //cout<<"----------\n";
        memset(sum2,0,sizeof(sum2));
        memset(sum1,0,sizeof(sum1));
        for(i=U;i--;){
            for(v=t[i];top[v]!=top[lca[i]];v=fa[top[v]]){
                add(top[v],depth[top[v]]-depth[lca[i]]+depth[s[i]]-depth[lca[i]]+1,X[i]);
                if(maxson[v])minu(maxson[v],depth[v]-depth[lca[i]]+depth[s[i]]-depth[lca[i]]+2,X[i]);
            }
            if(v!=lca[i]&&maxson[lca[i]]){
                add(maxson[lca[i]],depth[s[i]]-depth[lca[i]]+2,X[i]);
                if(maxson[v])minu(maxson[v],depth[s[i]]-depth[lca[i]]+depth[v]-depth[lca[i]]+2,X[i]);
            }
        }
        for(i=n;i;--i)
            if(top[i]==i){
                nowsum2=nowsum1=0;
                for(node=i;node;node=maxson[node])update(node);
            }
    }
    for(i=n;i;--i){
        p[i]=(p[i]+Mod)%Mod;
        badd(bl[i],p[i]),badd(br[i],-p[i]);
    }
    //回答询问 
    LL ans=0;
    while(Q--){
        in(u),in(v);
        ans=(bQ(bl[u])+bQ(bl[v]))%Mod;
        while(top[u]!=top[v])
            if(depth[top[u]]>depth[top[v]])u=fa[top[u]];
            else v=fa[top[v]];
        if(depth[u]>depth[v])swap(u,v);
        ans=((ans-bQ(bl[u])-bQ(bl[fa[u]]))%Mod+Mod)%Mod;
        printf("%d\n",ans);
    }
}

总结:
①以后比赛还剩个15min或20min的时候一定不要再写了,一定要给目前写出来的要交的程序出数据:出小数据、卡下界,出特殊数据、比如说链什么的,然后出一些可以手算的和随机的大数据。
②推式子的时候一定要仔细!
③一定要擅长使用函数,让代码和思路更清晰。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:158639次
    • 积分:3454
    • 等级:
    • 排名:第10891名
    • 原创:185篇
    • 转载:1篇
    • 译文:0篇
    • 评论:25条
    最新评论