P3210 [HNOI2010]取石头游戏
博弈论好题!
但是还没有完全弄懂...
主要的思想就是判掉“上三角”的情况,因为这种情况看起来比较劣,大家会把它放最后再选,并且一定会连续地选,即“先手”选第一个和第三个,“后手”选第二个.(理性理解失败,只能感性理解)
处理掉“上三角”以后,就可以直接从大到小选了。
然后会发现样例都 WA 了。这是因为边界可能出现靠边的位置比较大的情况,而这时如果有偶数个,那么肯定留在最后“被迫”选择;如果有奇数个,那么最靠里的那个不会受到影响。
值得注意的是,两边还可能出现“下三角”的情况,这时需要我们先拿走“向下”的那部分。
代码(栈):
signed main() {
read(n);
for (register int i = 1; i <= n; ++i) {
read(v[i]);
vis[i] = v[i] > 0;
if (vis[i]) ++tot;
sum += v[i];
}
bool flag = false;
for (register int i = 1; i <= n; ++i) {
if (!vis[i]) {
if (!flag) {
flag = true;
memcpy(tmp, stk, sizeof(stk));
memset(stk, 0, sizeof(stk));
top = stop; stop = 0;
} else {
while (stop) h[++htot] = stk[stop], stop--;
}
continue;
}
stk[++stop] = v[i];
while (stop > 2 && stk[stop - 1] >= stk[stop] && stk[stop - 1] >= stk[stop - 2])
--stop, stk[stop] = stk[stop + 1] + stk[stop - 1] - stk[stop], --stop, stk[stop] = stk[stop + 1];
}
while (top > 1 && tmp[top] >= tmp[top - 1]) h[++htot] = tmp[top], --top;
if (top <= 1) {
while (top) h[++htot] = tmp[top], top--;
} else {
for (register int i = 1; i + 1 <= top; i += 2) {
cha += (tot & 1 ? 1 : -1) * (tmp[i] - tmp[i + 1]);
}
if (top & 1) h[++htot] = tmp[top];
}
int st = 1;
while (st < stop && stk[st] >= stk[st + 1]) h[++htot] = stk[st], ++st;
if (stop - st + 1 <= 1) {
while (stop >= st) h[++htot] = stk[stop], --stop;
} else {
for (register int i = stop; i - 1 >= st; i -= 2) {
cha += (tot & 1 ? 1 : -1) * (stk[i] - stk[i - 1]);//Attention!!!!!
}
if ((stop - st + 1) & 1) h[++htot] = stk[st];
}
sort(h + 1, h + 1 + htot);
int tp = 1;
for (register int i = htot; i; --i) {
cha += tp * h[i];
tp *= -1;
}
printf("%lld %lld\n", (cha + sum) / 2, (sum - cha) / 2);
return 0;
}
刘队的简化版代码(双向链表):
int main()
{
read(n),r[0]=1,l[n+1]=n;
for(int i=1;i<=n;++i)
read(v[i]),sum+=v[i],l[i]=i-1,r[i]=i+1,tag[i]=(v[i]!=0);
for(int i=3;i<=n;i=r[i])
while(tag[l[l[i]]]&&tag[l[i]]&&tag[i]&&v[l[i]]>=v[l[l[i]]]&&v[l[i]]>=v[i])
v[i]=v[l[l[i]]]+v[i]-v[l[i]],r[l[l[l[i]]]]=i,l[i]=l[l[l[i]]];
L=r[0],R=l[n+1];
while(v[L]>=v[r[L]]&&tag[L]&&tag[r[L]]) s+=v[r[L]]-v[L],L=r[r[L]];
while(v[R]>=v[l[R]]&&tag[R]&&tag[l[R]]) s+=v[l[R]]-v[R],R=l[l[R]];
for(int i=L;i<=R;i=r[i])
if(tag[i])
v[++tot]=v[i];
sort(v+1,v+tot+1,cmp),v[++tot]=s;
for(int i=1;i<=tot;++i)
{
if(i&1) val+=v[i];
else val-=v[i];
}
printf("%lld %lld",(sum+val)/2,(sum-val)/2);
return 0;
}