考场写了一个神奇的树链剖分过了???
我的做法简单来讲就是按照题意模拟,对于一条边求出它在另一棵树上可以 b a n ban ban掉的边,然后对于每条重链开 v e c t o r vector vector线段树维护可以删掉的边,修改的时候参考标记永久化即可。
时间复杂度摊下来是 O ( n l o g n 2 ) O(nlogn^2) O(nlogn2)
本来一点都不毒瘤但本地测大数据的时候会爆栈于是写了个bfs版的
代码:
#include
#define ri register int
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
#define pb push_back
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=2e5+5;
priority_queue<int,vector<int>,greater<int> >q[2];
int n,que[N],hd,tl;
struct Node{int x,y;};
struct Tree{
vector<int>e[N],S[N<<2];
bool vis[N];
Node G[N];
int siz[N],fa[N],hson[N],dep[N],top[N],num[N],pred[N],tot;
Tree(){
tot=dep[1]=fa[1]=0;
memset(hson,0,sizeof(hson));
memset(G,0,sizeof(G));
memset(vis,0,sizeof(vis));
}
void bfs(){
memset(que,0,sizeof(que));
que[hd=tl=1]=1;
while(hd<=tl){
int p=que[hd++];
siz[p]=1;
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p])continue;
fa[v]=p,dep[v]=dep[p]+1,que[++tl]=v;
}
}
for(ri i=n,p;i>1;--i){
p=que[i],siz[fa[p]]+=siz[p];
if(siz[p]>siz[hson[fa[p]]])hson[fa[p]]=p;
}
for(ri i=1,p;i<=n;++i){
p=que[i];
if(hson[fa[p]]^p)top[p]=p;
else top[p]=top[fa[p]];
}
que[hd=tl=n]=1;
for(ri i=1;i<=n;++i){
int p=que[hd++];
pred[num[p]=++tot]=p;
if(!hson[p])continue;
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p]||v==hson[p])continue;
que[--hd]=v;
}
que[--hd]=hson[p];
}
}
inline int lca(int x,int y){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline void update(int p,int l,int r,int ql,int qr,int v){
if(ql>r||qr<l)return;
if(ql<=l&&r<=qr){S[p].pb(v);return;}
if(qr<=mid)update(lc,l,mid,ql,qr,v);
else if(ql>mid)update(rc,mid+1,r,ql,qr,v);
else update(lc,l,mid,ql,mid,v),update(rc,mid+1,r,mid+1,qr,v);
}
inline void query(int p,int l,int r,int k,int op){
for(int i=0;i<S[p].size();++i)if(!vis[S[p][i]])vis[S[p][i]]=1,q[op].push(S[p][i]);
if(l==r)return;
if(k<=mid)query(lc,l,mid,k,op);
else query(rc,mid+1,r,k,op);
}
inline void init(){
for(ri i=1;i<=n;++i)e[i].clear();
for(ri i=1;i<=(n<<2);++i)S[i].clear();
for(ri i=2,u,v;i<=n;++i)u=read(),e[u].pb(i),e[i].pb(u),G[i-1]=(Node){u,i};
bfs();
}
inline void change(int x,int y,int v){
if(x==y)return;
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(1,1,n,num[top[x]],num[x],v),x=fa[top[x]];
}
if(x==y)return;
if(dep[x]<dep[y])swap(x,y);
update(1,1,n,num[y]+1,num[x],v);
}
}T[2];
inline void solve(){
for(ri i=1,u,v,t;i<n;++i){
u=T[0].G[i].x,v=T[0].G[i].y,t=T[1].lca(u,v);
T[1].change(u,t,i),T[1].change(v,t,i);
}
for(ri i=1,u,v,t;i<n;++i){
u=T[1].G[i].x,v=T[1].G[i].y,t=T[0].lca(u,v);
T[0].change(u,t,i),T[0].change(v,t,i);
}
}
inline void ask(int id,int op){
int u=T[op].G[id].x,v=T[op].G[id].y;
if(T[op].dep[u]<T[op].dep[v])swap(u,v);
T[op].query(1,1,n,T[op].num[u],op^1);
}
int main(){
n=read();
T[0].init(),T[1].init();
int tmp=0;
q[tmp].push(read()),T[tmp^1].vis[q[tmp].top()]=1;
solve();
int tim=0;
while(q[tmp].size()){
++tim;
if(tmp==0)puts("Blue");
else puts("Red");
int cnt=0;
while(!q[tmp].empty()){
int x=q[tmp].top();
q[tmp].pop();
cout<<x<<' ',ask(x,tmp);
}
tmp^=1;
puts("");
}
return 0;
}
n , m ≤ 5 e 4 n,m\le5e4 n,m≤5e4
考虑用 + 1 / − 1 +1/-1 +1/−1来表示左右括号,然后相当于查和为 0 0 0的路径,考虑用点分治来合并两条路径,那么对于一个点对 ( i , j ) (i,j) (i,j),只用使得 s u m i , g + s u m j , g = 0 sum_{i,g}+sum_{j,g}=0 sumi,g+sumj,g=0即可,于是对于每个点到当前分治重心记录最大/小的前缀和是多少,且这个最值出现了多少次,于是可以将两个拼起来,即 a n s i + j + = f s u m i ∗ g − s u m j ans_{i+j}+=f_{sum}{i}*g_{-sum}{j} ansi+j+=fsumi∗g−sumj发现是一个卷积形式,于是上 f f t fft fft优化一波。
注意由于点分治走一层上限小一半因此复杂度并不是 O ( n 2 l o g n ) O(n^2logn) O(n2logn)而是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
代码:
#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
inline int readf(){
char ch=gc();
while(ch!='('&&ch!=')')ch=gc();
return ch==')'?-1:1;
}
typedef long long ll;
const int N=2e5+5;
int n,m,a[N],ans[N];
vector<int>e[N];
struct cp{
double x,y;
cp(double x=0,double y=0):x(x),y(y){}
friend inline cp operator+(const cp&a,const cp&b){return cp(a.x+b.x,a.y+b.y);}
friend inline cp operator-(const cp&a,const cp&b){return cp(a.x-b.x,a.y-b.y);}
friend inline cp operator*(const cp&a,const cp&b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
friend inline cp operator/(const cp&a,const double&b){return cp(a.x/b,a.y/b);}
};
vector<cp>A,B;
vector<int>pos,f[N<<1],g[N<<1];
int lim,tim;
inline void init(const int&up){
lim=1,tim=0;
while(lim<=up)lim<<=1,++tim;
pos.resize(lim),A.resize(lim),B.resize(lim),pos[0]=0;
for(ri i=0;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
}
const double pi=acos(-1.0);
inline void fft(vector<cp>&a,const int&type){
for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
cp w,wn,a0,a1;
for(ri mid=1;mid<lim;mid<<=1){
wn=cp(cos(pi/mid),sin(pi/mid)*type);
for(ri j=0,len=mid<<1;j<lim;j+=len){
w=cp(1,0);
for(ri k=0;k<mid;++k,w=w*wn){
a0=a[j+k],a1=a[j+k+mid]*w;
a[j+k]=a0+a1,a[j+k+mid]=a0-a1;
}
}
}
if(type==-1)for(ri i=0;i<lim;++i)a[i]=a[i]/lim;
}
int siz[N],all,rt,ms,du;
bool vis[N];
void getroot(int p,int fa){
siz[p]=1;
int mss=0;
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa||vis[v])continue;
getroot(v,p),siz[p]+=siz[v],mss=max(mss,siz[v]);
}
mss=max(mss,all-siz[p]);
if(mss<ms)rt=p,ms=mss;
}
inline void calc(int x,int type){
int nn=f[all+x].size(),mm=g[all-x].size(),n=0,m=0;
if(!nn||!mm)return;
for(ri i=0;i<nn;++i)n=max(n,f[all+x][i]);
for(ri i=0;i<mm;++i)m=max(m,g[all-x][i]);
init(n+m);
for(ri i=0;i<lim;++i)A[i]=B[i]=cp(0,0);
for(ri i=0;i<nn;++i)++A[f[all+x][i]].x;
for(ri i=0;i<mm;++i)++B[g[all-x][i]].x;
fft(A,1),fft(B,1);
for(ri i=0;i<lim;++i)A[i]=A[i]*B[i];
fft(A,-1);
for(ri i=0;i<lim;++i)ans[i+(x!=0)]+=(ll)type*(ll)(A[i].x+0.5);
}
void dfs1(int p,int fa,int pre,int mx,int cnt){
pre+=a[p],++du;
if(!pre)++cnt;
else if(pre==1)pre=cnt=0,++mx;
if(!pre)f[all+mx].push_back(cnt);
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa&&!vis[v])dfs1(v,p,pre,mx,cnt);
}
void dfs2(int p,int fa,int pre,int mx,int cnt){
pre+=a[p];
if(!pre)++cnt;
else if(pre==-1)pre=cnt=0,--mx;
if(!pre)g[all+mx].push_back(cnt);
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa&&!vis[v])dfs2(v,p,pre,mx,cnt);
}
inline void delet(int p,int pre){
du=1;
if(pre==1)dfs1(p,0,0,1,0);
else dfs1(p,0,-1,0,0);
dfs2(p,0,0,0,0);
for(ri i=0;i<=du;++i)calc(i,-1),f[all+i].clear(),g[all-i].clear();
}
inline void solve(int p){
vis[p]=1;
dfs1(p,0,0,0,0);
int S=all;
for(ri i=0,v;i<e[p].size();++i)if(!vis[v=e[p][i]])dfs2(v,p,0,0,0);
g[S].push_back(0);
for(ri i=0;i<=S;++i)calc(i,1),f[S+i].clear(),g[S-i].clear();
for(ri i=0,v;i<e[p].size();++i)if(!vis[v=e[p][i]])delet(v,a[p]);
for(ri i=0,v;i<e[p].size();++i){
if(vis[v=e[p][i]])continue;
all=siz[v]>siz[p]?S-siz[p]:siz[v],ms=n+1,getroot(v,p),solve(rt);
}
}
int main(){
n=read();
for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
for(ri i=1;i<=n;++i)a[i]=readf();
ms=all=n,getroot(1,0),solve(rt);
ll sum=(ll)n*n;
for(ri i=1;i<=n;++i)sum-=ans[i];
ans[0]=sum;
for(ri tt=read();tt;--tt)cout<<ans[read()]<<'\n';
return 0;
}
这个是湖南2013省选模拟原题,神仙结论+费用流。