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;
}
(终于知道这次为什么掉这么多分了,连板子题都没过)
浙公网安备 33010602011771号