【NOIP/CSP2019】D1T2 括號樹

原題:ios

 

 

由於是NOIP題,因此首先先看特殊數據,前35分是一條長度不超過2000的鏈,N^2枚舉全部子區間暴力check就能拿到分ide

其次能夠思考特殊狀況,一條鏈的狀況怎麼作spa

OI系列賽事的特殊性質分不少時候不只是幫助得分,還幫助選手找到思路code

觀察合法串的形狀,能夠發現主要由括號嵌套和並列組成blog

嵌套好說,一對匹配的括號對答案貢獻爲1(裏邊包的東西不合法的括號不算匹配的括號)ci

對於並列的括號,能夠發現若是要把兩對匹配的括號並列算做一個貢獻,那麼必需要求這兩個括號挨着,即右邊的左括號的左邊是左邊的右括號get

思考涉及到子區間的問題時,一個常見的思路是肯定一個端點,考慮另外一個string

由於括號匹配是從左到右添加進棧的,那麼不妨肯定右端點,對於加入的右括號咱們只需考慮其能匹配多少個左端點it

能夠發現,某個右括號和跟他匹配的左括號算1個貢獻io

若是左括號的左邊是匹配上的右括號,那麼這個右括號做爲右端點的合法區間均可以直接接上右邊匹配的一對括號算做貢獻1

那麼思路就很清楚了,總結一下,只考慮對於匹配上的右括號,有多少個左端點使得區間合法

它本身的左括號算貢獻1,而後再把右端點爲左括號下標-1的合法區間數接上

用g[i]表示點i爲右端點的方案數,f[i]表示g[i]的前綴和(用於統計答案)

那麼若是某個點是左括號或匹配不上的右括號,g[i]爲0

若是是匹配上左括號j的右括號,g[i]=g[j-1]+1

那麼對於一棵樹的狀況,其實能夠發現,只須要dfs樹,而後回溯的時候把新加進來的括號退棧就跟序列的狀況沒什麼區別

(固然別忘了剛纔退棧的括號回溯時要補進)

 

代碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 #define LL long long
 8 int rd(){int z=0,mk=1;  char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-')mk=-1;  ch=getchar();}
10     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
11     return z*mk;
12 }
13 struct edg{int nxt,y;}e[510000];  int lk[510000],ltp=0;
14 void ist(int x,int y){  e[++ltp]=(edg){lk[x],y};  lk[x]=ltp;}
15 int n,fth[510000];  char s[510000];
16 LL f[510000],g[510000];
17 int q[510000],hd=0;
18 void dfs(int x){
19     //cout<<"x: "<<x<<" f: "<<f[x]<<" g: "<<g[x]<<endl;
20     //for(int i=1;i<=hd;++i)  cout<<q[i]<<" ";
21     //cout<<endl;
22     for(int i=lk[x];i;i=e[i].nxt){
23         int tmp=q[hd];
24         if(s[e[i].y]==')' && hd){
25             g[e[i].y]=g[fth[tmp]]+1;
26             f[e[i].y]=f[x]+g[e[i].y];
27             --hd;
28         }
29         else{
30             if(s[e[i].y]=='(')  q[++hd]=e[i].y;
31             //由於棧裏只會有左括號,因此存下標表示這裏有個左括號就vans了
32             g[e[i].y]=0;
33             f[e[i].y]=f[x];
34         }
35         dfs(e[i].y);
36         if(s[e[i].y]==')' && tmp)  q[++hd]=tmp;
37         //注意不是hd!=0
38         else if(s[e[i].y]=='(')  --hd;
39     }
40 }
41 int main(){
42     freopen("ddd.in","r",stdin);
43     cin>>n;
44     scanf("%s",s+1);
45     for(int i=2;i<=n;++i)  fth[i]=rd(),ist(fth[i],i);
46     //q[++hd]=1;  f[1]=0,g[1]=0;  注意s[1]不必定是'('
47     if(s[1]=='(')  q[++hd]=1;  f[1]=0,g[1]=0;
48     dfs(1);
49     LL ans=0;
50     for(int i=1;i<=n;++i)  ans^=i*f[i];
51     cout<<ans<<endl;
52     return 0;
53 }
View Code
相關文章
相關標籤/搜索