[数学] Codechef September Challenge 2017 Weasel does Xor on Tree

fi,j 表示时间为 i 的时候,j 点的权值

那么 fi,u=XOR{fi1,v|vsonu}

fi1,v fi1,v 带入不断展开

就可以得到 fx,1=XOR{aif0,i} ,其中 aif0,i 表示 ai f0,i 异或起来

手动展开一下发现这个系数就跟这个点的深度有关,可以递推得到

并且 ai 的递推形式就跟组合数的递推形式一样。

推一推就可以知道 ai=(depth+x2depth1)

显然当 ai 为奇数的时候,点 i 才有贡献。

考虑lucas定理,也就是说 depth1 的二进制要是 depth+x2 的子集

再转化就是 x1 depth1 没有重合,也就是and起来为0

然后就 3logn 子集统计一下就好了

x 很大,但是只有 x 前面的一段二进制位是有用的, x 2log2n 取模就可以了

UPD:子集统计可以用高维前缀和,复杂度就降到了 nlogn

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N=1000010;

int n,m,cnt,imax,fa[N],dpt[N],G[N];
ll a[N];
struct iedge{
    int t,nx;
}E[N<<1];

inline void addedge(int x,int y){
    E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
    E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt;
}

int it;

void dfs(int x,int f){
    fa[x]=f; dpt[x]=dpt[f]+1;
    imax=max(imax,dpt[x]);
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f) dfs(E[i].t,x);
}
inline int C(int x,int y){
    if(!x && !y) return 1;
    if(!(x&1) && (y&1)) return 0;
    return C(x/2,y/2);
}

ll f[N],g[N];

void PutAns(ll x){
    if(x>=10) PutAns(x/10); putchar(x%10+'0');
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1,x,y;i<n;i++)
        scanf("%d%d",&x,&y),addedge(x+1,y+1);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    dfs(1,0);
    int NN=1; while(NN<imax) NN<<=1; NN--;
    for(int i=1;i<=n;i++) f[dpt[i]-1]^=a[i];
    /*for(int i=0;i<=NN;i++){
        g[i]^=f[0];
        for(int j=NN-i;j;j=(j-1)&(NN-i)) 
            g[i]^=f[j];
    }
    while(m--){
        ll x; scanf("%lld",&x);
        if(x==0) PutAns(a[1]);
        else PutAns(g[(x-1)%(NN+1)]); putchar('\n');
    }*/

    for(int i=0;i<=17;i++)
        for(int j=0;j<=NN;j++)
            if(~j>>i&1) f[j|(1<<i)]^=f[j];
    while(m--){
        ll x; scanf("%lld",&x);
        if(x==0) PutAns(a[1]);
        else PutAns(f[NN-(x-1)%(NN+1)]); putchar('\n');
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值