提高组比赛后我差点抱头痛哭——
亲眼看着民间数据别人300+,AC了三题,我200-,爆零了三题。。
尤其是看了测评上的分数,一道打了2h+的血汗题爆零了。。
(污污污)。。。。。
注意,本博文为比赛总结,不一定有题解
这道题比较简单,按题意倒过去推就可以,难点在于它的数据范围
由题可知,k<2^64,用long long都不行,如果还是想直接存储,只能用unsigned long long,还必须提心吊胆地用。
由题意可知,合理括号串有两种(设大写字母为一个合理括号串):
两种串意味着两种计算方法,第一种,numA=numB+1,第二种,
很容易想到,用一个结构体表示一个括号串(总和sum,项数n),然后用栈存到根节点的路径上的所有分隔开的括号串(以一个不含合理串的不合理串分隔,如“(”),分隔开的若干个串只用单独计算,互不关联,
当一个节点的括号进入到判断栈(起码还要有个栈来判断合理吧)中,与上一个括号匹配,还需要分类讨论:
在入栈之前,还要特判一下:因为两个括号匹配后,可能导致两个原本被分隔开的合理串变为相邻(如"()(()"加上一个")",经上述操作后变为"()(())",红绿两串相邻,应合为第二类括号串),此时要把栈顶踢出与将进入的串合并,再入栈。
以此计算每个节点值的时候,还要考虑一下回溯。
因为一条路径搜完过后还要回来搜另一条,所以必须把两个栈还原。
还原可以打一个大的结构体直接赋值吗?
不行!因为我就是这样才扣了50
由于判断栈每一次要么出一个括号,要么进一个括号,所以只用记录操作类型和改变的值;
大栈里在每次入栈一个新串之前,要么不改动,要么踢了一个串,要么踢了两个串,记录踢出的串(最多两个)然后还原。
时间复杂度约为O(n)
#include//整篇博客光秃秃,拼个代码丰富一下内容
#include//大佬的代码60行,我的行数为两倍
#include
#include
#include
#include
#include
#define M 500005
#define ll long long
using namespace std;
int n,fa[M];
ll k[M];
vectorG[M];
bool c[M],spcl;
char s;
stackst1;
inline int Read(){
int x=0;char s=getchar();
while(s<'0'||s>'9'){s=getchar();}
while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+s-'0';s=getchar();}
return x;
}
struct itn{
int l,r;
ll NUM,SUM;
itn(){l=r=NUM=SUM=0;}
itn(int L,int R){
l=L,r=R;
}
};
struct bol{
stackS;
ll ans;
}st2;
void dfs(int x){
itn yst1,yst2;
if(!st2.S.empty())yst1=st2.S.top();
if(st2.S.size()>1){
st2.S.pop();yst2=st2.S.top();
st2.S.push(yst1);
}
ll rt=st2.S.size(),yan=st2.ans;
int out=-1,jin=-1;
if(st1.empty()||c[st1.top()]==c[x]){
st1.push(x);
jin=1;
k[x]=k[fa[x]];
}
else{
if(c[st1.top()]==0){
int l=st1.top();
itn in=itn(l,x);
st1.pop();
out=l;
if(l!=fa[x]){
itn A=st2.S.top();
st2.S.pop(),st2.ans-=A.SUM+1ll*(A.NUM*(A.NUM-1ll)>>1);
in.NUM=1ll,in.SUM=1ll+(A.SUM+1ll*(A.NUM*(A.NUM-1ll)>>1));
}
else{
in.NUM=1ll,in.SUM=1ll;
}
if(st2.S.empty()||st2.S.top().r!=fa[l]){
st2.S.push(in);
st2.ans+=in.SUM+1ll*(in.NUM*(in.NUM-1ll)>>1);
}
else{
itn A=st2.S.top();
st2.S.pop(),st2.ans-=A.SUM+1ll*(A.NUM*(A.NUM-1ll)>>1);
A.r=x,A.NUM+=in.NUM,A.SUM+=in.SUM;
st2.S.push(A);
st2.ans+=A.SUM+1ll*(A.NUM*(A.NUM-1ll)>>1);
}
k[x]=st2.ans;
}
else{
st1.push(x);
jin=1;
k[x]=k[fa[x]];
}
}
for(int i=0;i0)st1.pop();
if(out>0)st1.push(out);
while(st2.S.size()>rt)st2.S.pop();
if(yst2.l>0){
while(st2.S.size()>rt-2)st2.S.pop();
st2.S.push(yst2),st2.S.push(yst1);
}
else{
if(yst1.l>0){
while(st2.S.size()>rt-1)st2.S.pop();
st2.S.push(yst1);
}
}
st2.ans=yan;
}
}
int main()
{
//freopen("brackets.in","r",stdin);
//freopen("brackets.out","w",stdout);
n=Read();
for(int i=1;i<=n;i++){
s=getchar();
while(s!='('&&s!=')')s=getchar();
c[i]=(s==')');
}
for(int i=2;i<=n;i++){
fa[i]=Read();
if(fa[i]!=i-1)spcl=1;
G[fa[i]].push_back(i);
}
dfs(1);
ll ans=k[1];
for(ll i=2;i<=1ll*n;i++)ans^=(i*k[i]);
printf("%lld\n",ans);
return 0;
}
我还没搞懂,只截了张PPT
这就是我的爆零的血汗题
&%&…@%#@%¥%#(*#*&@T@(#*#^$*#(FUCK)#(*&$^^%*&&@#...
看到这题,我想到了容斥,还想到了dp式...仿佛看到了骗84分的曙光...
但式子打错了,100多行代码,编译器又不能调,最后检查不出来......
我对这道题太执着,策略严重错误
唉。。。
这两道题虽然难,但有脑的人应该可以骗到100+的分(AK大佬除外)
只要拿到部分分数,总分就会比那些死敲正解还爆零的人(比如我)要高得多
经过惨痛教训的思考后我深刻意识到,除AK虐场的大佬们外,暴力骗分是我们小ju拿省一的关键一招&关键抉择&决定性因素&唯一途径!
不要以为有了想法就能打AC(大佬除外)
要把暴力骗分卡常和优化(还有手速)的基本功练到极致!