【點分治】括號(2022.5.28)
阿新 • • 發佈:2022-05-30
題目
2.1 題目描述
有一棵n 個點的無向樹,每個點上有一個標記,為'('或')',對於一個有序點對 (u,v),若
從 u 到 v (包含u,v)的有向路徑上的所有標記組成了一個合法的括號序列,則稱這是一
個完美點對,請求出有多少個完美點對。
2.2 輸入格式
第一行為一個整數 n,表示點數;
第二行為一個由’(‘和’)’組成的字串,表示每個點上的標記;
接下來n-1 行,每行兩個整數u, v,表示u,v 之間有一條無向邊。
2.3 輸出格式
輸出一行一個整數,表示完美點對的數目。
2.4 樣例輸入
4
(())
1 2
2 3
3
2.5 樣例輸出
2
2.6 資料範圍與約定
對於前20%的資料 n≤100;
對於100%的資料,1≤n≤105。
解思
第一眼看感覺像是點分治,覺得應該不會考來著於是就先看第三題去了(最後打暴力寫炸了..)
沒想到真的是點分治
算是點分治的模板題吧,每層找重心然後統計過重心的路徑貢獻
注意此題括號序列是否成立的細節
上程式碼!
#include<bits/stdc++.h> using namespace std; inline int read(){ int f=1,j=0;char w=getchar(); while(w>'9'||w<'0'){ if(w=='-')f=-1; w=getchar(); } while(w>='0'&&w<='9'){ j=(j<<3)+(j<<1)+w-'0'; w=getchar(); } return f*j; } const int N=100001,T=100000; int head[N],to[N*2],front[N*2],tail; int n,sum[N],cnt[N*3][2],size[N],mid[N*3][2];//cnt->0:'('starts cnt->1:')'starts int root_sum,maxn,minn,maxn1,minn1; long long ans; bool walk[N]; char s[N]; void addline(int x,int y){ to[++tail]=y; front[tail]=head[x]; head[x]=tail; return ; } void getsize(int nown,int fa){ size[nown]=1; for(int k=head[nown];k;k=front[k]){ int x=to[k]; if(x==fa||walk[x])continue; getsize(x,nown); size[nown]+=size[x]; } return ; } int getcore(int nown,int fa,int all_size){ for(int k=head[nown];k;k=front[k]){ int x=to[k]; if(walk[x]||x==fa)continue; if(size[x]>all_size/2)return getcore(x,nown,all_size); } return nown; } void getcnt(int nown,int fa,int nowsum,int ll,int rr){ nowsum+=sum[nown]; if(ll<0&&sum[nown]>0)ll++; if(sum[nown]<0)ll--; if(rr>0&&sum[nown]<0)rr--; if(sum[nown]>0)rr++; if(nowsum<=0&&sum[nown]<0&&rr==0){ ans+=cnt[T-nowsum][0]; mid[T+nowsum-root_sum][1]++; minn1=min(minn1,T+nowsum-root_sum); maxn1=max(maxn1,T+nowsum-root_sum); } if(nowsum>=0&&sum[nown]>0&&ll==0){ ans+=cnt[T-nowsum][1]; mid[T+nowsum-root_sum][0]++; minn1=min(minn1,T+nowsum-root_sum); maxn1=max(maxn1,T+nowsum-root_sum); } for(int k=head[nown];k;k=front[k]){ int x=to[k]; if(x==fa||walk[x])continue; getcnt(x,nown,nowsum,ll,rr); } return ; } void work(int nown){ getsize(nown,0); int core=getcore(nown,0,size[nown]); walk[core]=true; for(int k=head[core];k;k=front[k]){ int x=to[k]; if(walk[x])continue; work(x); } walk[core]=false; root_sum=sum[core]; maxn=T;minn=T; cnt[T][0]=cnt[T][1]=1; for(int k=head[core];k;k=front[k]){ int x=to[k]; if(walk[x])continue; maxn1=T;minn1=T; getcnt(x,core,root_sum,0,0); for(int i=minn1;i<=maxn1;i++)cnt[i][0]+=mid[i][0],cnt[i][1]+=mid[i][1],mid[i][0]=mid[i][1]=0; minn=min(minn,minn1); maxn=max(maxn,maxn1); } for(int i=minn;i<=maxn;i++)cnt[i][0]=cnt[i][1]=0; return ; } signed main(){ n=read(); scanf("%s",s+1); for(int i=1;i<=n;i++){ if(s[i]=='(')sum[i]=1; else sum[i]=-1; } for(int i=1;i<n;i++){ int x=read(),y=read(); addline(x,y);addline(y,x); } work(1); printf("%lld",ans); return 0; }