没有题面,没有链接。node
2019 csp-s day1 t2c++
说真的我当时考场上真的连这个题的暴力都没想到spa
暴力的思路很简单,由于是链,能够依次枚举当前点i,而后枚举区间,判断这个区间是否合法,若是合法,ans++;code
复杂度是\(O(n^4)\),实际并跑不满;递归
#include<bits/stdc++.h> #define ll long long using namespace std; inline ll read() { ll ans=0; char last=' ',ch=getchar(); while(ch>'9'||ch<'0') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } ll n; char a[500010]; char Getchar() { char ch; do { ch=getchar(); }while(ch!=')'&&ch!='('); return ch; } ll ans; ll fa[500010]; bool check(int l,int r) { int top=0; for(int i=l;i<=r;i++) { if(a[i]=='(') top++; else { if(top>0) top--; else return 0; } } return top==0?1:0; } int main() { n=read(); for(int i=1;i<=n;i++) a[i]=Getchar(); for(int i=1;i<n;i++) fa[i+1]=read(); ll k; for(int i=1;i<=n;i++) { k=0; for(int l=1;l<i;l++) { for(int r=l+1;r<=i;r++) { if(check(l,r)) k++; } } ans^=(k*i); } printf("%lld",ans); return 0; }
定义一个lst[i],记录从1~i,以i结尾的合法的括号序列有多少个get
对于一个'('来讲,显然lst[i]=0;
因此考虑右括号:it
举例子手玩一下:io
括号√ | ( | ) | ) | ( | ) | ) | ( | ( | ) |
---|---|---|---|---|---|---|---|---|---|
lst | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
sum | 0 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 3 |
括号√ | ( | ) | ( | ( | ( | ) | ) | ) | ) |
---|---|---|---|---|---|---|---|---|---|
lst | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 2 | 0 |
sum | 0 | 1 | 1 | 1 | 1 | 2 | 3 | 5 | 5 |
能够发现,当一个括号是右括号(记位置为i)的时候,首先要找是否有匹配的左括号。table
若是没有能够匹配的左括号,lst=0;ast
若是有了匹配的左括号,lst的值至少为1(显然中间的部分都是合法的说 感性李姐,
而后再考虑匹配的左括号左边是否能够与当前的括号再拼成其余的合法括号序列,这个时候咱们就要看匹配的左括号的左边一个的lst(记这个位置为t-1),显然的,若是以t-1s结尾的括号能够拼成合法的括号序列,那么它与刚刚匹配的那段序列一样能够组成一个以最后的右括号结尾的合法序列。
所以,\(lst[i]=lst[t-1]+1;\)
用一个栈来维护左括号是再好不过了:遇到左括号压入栈中,若是碰到一个匹配的右括号,退栈。
因而:
\(lst[i]=lst[t-1]+1,t=s[top]\\ans[i]=ans[i-1]+lst[i]\)
因而这就是链的部分分:
#include<bits/stdc++.h> #define ll long long using namespace std; inline ll read() { ll ans=0; char last=' ',ch=getchar(); while(ch>'9'||ch<'0') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } ll n; char a[500010]; char Getchar() { char ch; do { ch=getchar(); }while(ch!=')'&&ch!='('); return ch; } ll Ans; ll fa[500010]; ll lst[500010],s[500010]; ll ans[500010]; ll top; int main() { n=read(); for(int i=1;i<=n;i++) a[i]=Getchar(); for(int i=1;i<n;i++) fa[i+1]=read(); for(int i=1;i<=n;i++) { if(a[i]==')') { if(top!=0) { int t=s[top]; top--; lst[i]=lst[t-1]+1; } } else s[++top]=i; ans[i]=ans[i-1]+lst[i]; Ans^=ans[i]*i; } printf("%lld",Ans); return 0; }
考虑把链上的dp转移到树上:
和在链上的dp很相似,由于是在树上,所以只须要将dp式子中的\(t-一、i-1\)替换成\(fa[t]、fa[i]\)便可;
大体以下:
\(lst[u]=lst[fa[t]]+1,t=s[top];\\ans[u]=ans[fa[u]]+lst[u]\)
而后考虑应该如何维护s:
一路向下递归,显然递归获得的是一条链,所以往下递归的时候,按照链的递归方法可劲加就好,主要须要注意的在递归到底之后的回溯上:
\(\mathbb{A}.\)若是当前节点是一个右括号,
\(\mathfrak{a}.\)在这条链上,这个右括号找到了一个匹配的左括号。
显然,左括号会在与右括号匹配过程当中从栈中弹出,而当咱们要回溯时,被弹出的节点要从新压入栈中,来与其余子树中的右括号匹配,
\(\mathfrak{b}.\)固然,若是这个点没有找到能够匹配的左括号,咱们天然不须要再进栈。
\(\mathbb{B}.\)若是当前节点是左括号,那么须要将当前节点退栈。
关于建图的话,由于给出的是明确的父亲儿子关系,所以能够只建单向边。
#include<bits/stdc++.h> #define ll long long using namespace std; inline ll read() { ll ans=0; char last=' ',ch=getchar(); while(ch>'9'||ch<'0') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } char Getchar() { char ch; do { ch=getchar(); }while(ch!=')'&&ch!='('); return ch; } const int mxn=500010; ll n,Ans; char a[mxn]; ll lst[mxn],ans[mxn]; ll s[mxn],top; struct node { int to,nxt; }e[mxn<<1]; ll ecnt,head[mxn],fa[mxn]; void add(int from,int to) { ++ecnt; e[ecnt].to=to; e[ecnt].nxt=head[from]; head[from]=ecnt; } void dfs(int u) { int t=0; if(a[u]=='(') //左括号,入栈 s[++top]=u; else {//右括号 if(top!=0) {//能够找到匹配的左括号 t=s[top]; lst[u]=lst[fa[t]]+1; top--;//退栈 } } ans[u]=ans[fa[u]]+lst[u]; Ans^=ans[u]*u; for(int i=head[u],v;i;i=e[i].nxt) { v=e[i].to; dfs(v); } if(t!=0) //A: a or b s[++top]=t; if(a[u]=='(') //B top--; } int main() { n=read(); for(int i=1;i<=n;i++) a[i]=Getchar(); for(int v=2,u;v<=n;v++) { u=read(); fa[v]=u; add(u,v); } dfs(1); printf("%lld",Ans); return 0; }
\(\color{Gold}{\mathfrak{To\ be\ a \ better \ person,with \ YkY}}\)