AtCoder Beginner Contest 291

G

居然没想到卷积!先拆位,原本一直在想对应位置都为0的个数如何用数据结构维护,并不可做。但只要把A或B翻转过来,把A和B的0都置为1,1置为0,卷积之后n-1的位置就是对应位置都是0的个数;对于序列平移,因为平移的是B,为了避免混乱,就把A翻转,然后B后延一倍长,对应从n-1到2n-2的位置即为各个答案。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<22)+5,P=998244353,G[2]={3,(P+1)/3};
inline int fpw(int a,int x){
    int s=1;
    for(;x;x>>=1,a=1ll*a*a%P) if(x&1) s=1ll*s*a%P;
    return s;
}
int rv[N],gp[2][N],iv[N],fc[N],fv[N];
void init(int n){
	fc[0]=1;
    for(int i=1;i<n;i++) iv[i]=fpw(i,P-2),fc[i]=1ll*fc[i-1]*i%P;
    fv[n-1]=fpw(fc[n-1],P-2);
    for(int i=n-2;~i;i--) fv[i]=1ll*fv[i+1]*(i+1)%P;
    for(int p=0;p<2;p++){
        for(int i=1;i<n;i<<=1){
            gp[p][i]=1;
            int t=fpw(G[p],(P-1)/(i<<1));
            for(int j=i+1;j<(i<<1);j++) gp[p][j]=1ll*gp[p][j-1]*t%P;
        }
    }
}
void print(char name[],int n,int* a){
	printf("%s n=%d\n",name,n);
	for(int i=0;i<n;i++) cout<<a[i]<<" "; puts(""); 
}
inline void dft(int* a,int n,int p){
    for(int i=0;i<n;i++) if(i<rv[i]) swap(a[i],a[rv[i]]);
    for(int i=1;i<n;i<<=1){
        for(int j=0;j<n;j+=(i<<1)){
            for(int k=0;k<i;k++){
                int &A=a[i+j+k],&B=a[j+k],t=1ll*gp[p][i+k]*A%P;
                A=B-t; if(A<0) A+=P;
                B=B+t; if(B>=P) B-=P;
            }
        }
    }
    if(p) for(int i=0;i<n;i++) a[i]=1ll*a[i]*iv[n]%P;
}
inline int Rev(int m){
    int p=0,n=1;
    while(n<m) n<<=1,p++;
    for(int i=0;i<n;i++) rv[i]=(rv[i>>1]>>1)|((i&1)<<(p-1));
    return n;
}
inline void poly_mul(int m,int* A,int* B,int* C){
    int n=Rev(m);
    dft(A,n,0); dft(B,n,0);
    for(int i=0;i<n;i++) C[i]=1ll*A[i]*B[i]%P;
    dft(C,n,1);
    fill(C+m,C+n,0);
}
//C=A*B m:需要C的0~(m-1)项,m~(n-1)为空
int n,a[N],b[N];
int ans[N];
int main(){
	init(1<<22);
	cin>>n;
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	for(int i=0;i<n;i++) scanf("%d",&b[i]);
	for(int p=1;p<=16;p<<=1){
		//cout<<"p="<<p<<endl;
		int A[N]={0},B[N]={0},C[N]={0};
		for(int i=0;i<n;i++){
			if(b[i]&p) A[n-i-1]=0;
			else A[n-i-1]=1;
		}
		for(int i=0;i<n;i++){
			if(a[i]&p) B[i]=B[i+n]=0;
			else B[i]=B[i+n]=1;
		}
		//print("A",n+n,A);
		//print("B",n+n,B);
		//print("C",n+n,C);
		poly_mul(n<<2,A,B,C);
		for(int i=n-1;i<n+n-1;i++){
			//cout<<C[i]<<" ";
			ans[i-n+1]+=(n-C[i])*p;
			//cout<<ans[i-n+1]<" ";
		}
		puts("");
	}
	int mx=0;
	for(int i=0;i<n;i++) mx=max(mx,ans[i]);
	cout<<mx<<endl;
}

Ex

题目的描述就是点分树的两个性质,因此直接构造点分树即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int hd[N],to[N<<1],nx[N<<1],tt;
inline void add(int u,int v){
    nx[++tt]=hd[u];
    to[hd[u]=tt]=v;
}
int n,rt,su,sz[N],mx[N];
bool vs[N];
void find(int u,int fa){
    sz[u]=1; mx[u]=0;
    for(int e=hd[u];e;e=nx[e]) if(to[e]!=fa && !vs[to[e]]){
            int v=to[e];
            find(v,u);
            sz[u]+=sz[v];
            mx[u]=max(mx[u],sz[v]);
        }
    mx[u]=max(mx[u],su-sz[u]);
    if(mx[u]<mx[rt]) rt=u;
    //cout<<"find:"<<u<<" "<<sz[u]<<" "<<mx[u]<<" su="<<su<<endl;
}
void cnt_size(int u,int fa){
    sz[u]=1; //mx[u]=0;
    for(int e=hd[u];e;e=nx[e]) if(to[e]!=fa && !vs[to[e]]){
            int v=to[e];
            cnt_size(v,u);
            sz[u]+=sz[v];
            //mx[u]=max(mx[u],sz[v]);
        }
    //mx[u]=max(mx[u],su-sz[u]);
    //cout<<"cnt_size:"<<u<<" "<<sz[u]<<" "<<mx[u]<<endl;
}
int ans[N];
void dfz(int u,int fa){
    vs[u]=1;
    cnt_size(u,fa);
    //cout<<"dfz:"<<u<<" "<<fa<<endl;
    for(int e=hd[u];e;e=nx[e]) if(to[e]!=fa && !vs[to[e]]){
            int v=to[e];
            su=sz[v]; rt=0;
            find(v,u);
            ans[rt]=u;
            //cnt_size(rt,u);
            //cout<<"v="<<v<<" "<<rt<<" "<<mx[v]<<" "<<sz[v]<<endl;
            dfz(rt,u);
        }
}
int main()
{
    cin>>n;
    for(int u,v,i=1;i<n;i++) scanf("%d%d",&u,&v),add(u,v),add(v,u);
    su=n; rt=0; mx[0]=n;
    find(1,0); //cnt_size(rt,0);
    //cout<<"rt="<<rt<<endl;
    ans[rt]=-1;
    dfz(rt,0);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]); puts("");
    return 0;
}

(终于知道这次为什么掉这么多分了,连板子题都没过)

posted @ 2023-03-02 21:24  sz[sz]  阅读(33)  评论(0)    收藏  举报