VIP-拔钉子之路-with ydnhaha-解题报告

i207M

2018-10-26 15:54:24

Personal

## [POI2016]Nim z utrudnieniem A和B两个人玩游戏,一共有m颗石子,A把它们分成了n堆,每堆石子数分别为a[1],a[2],...,a[n],每轮可以选择一堆石子,取掉任意颗石子,但不能不取。谁先不能操作,谁就输了。在游戏开始前,B可以扔掉若干堆石子,但是必须保证扔掉的堆数是d的倍数,且不能扔掉所有石子。A先手,请问B有多少种扔的方式,使得B能够获胜。 第一行包含两个正整数n,d(1<=n<=500000,1<=d<=10)。 第二行包含n个正整数a[1],a[2],...,a[n],(1<=a[i]<=1000000)。 本题中m不直接给出,但是保证m<=10000000。 首先将题意转化为,从n个数中选取d的倍数个,使得异或出来等于s的方案数; 暴力的DP:$f[i][j][k]$表示前i个选了%d=j个,异或为k的方案数,感觉过不去? 先考虑时间:我们按a升序排序,这样异或出来的最大数不会超过2a,所以总复杂度$O(dm)$ 空间:滚动数组都过不去!于是用tmp记一下$f[i-1][0][...]$即可; ``` signed main() { #ifdef M207 freopen("in.in","r",stdin); #endif in(n),in(d); dm=d-1; for(ri i=1;i<=n;++i) in(a[i]),s^=a[i]; sort(a+1,a+1+n); f[0][0]=1; for(ri i=1,mx=1;i<=n;++i) { while(mx<=a[i]) mx<<=1; for(ri j=0;j<mx;++j) tmp[j]=f[dm][j]; for(ri j=d-1,t;j>=1;--j) { t=j-1; for(ri k=0;k<mx;++k) add(f[j][k],f[t][k^a[i]]); } for(ri k=0;k<mx;++k) add(f[0][k],tmp[k^a[i]]); } printf("%d\n",(f[0][s]-(n%d==0)+md)%md); return 0; } ``` ## [POI2004]MOS 一个夜晚一些旅行者想要过桥. 他们只有一个火把. 火把的亮光最多允许两个旅行者同时过桥. 没有火把或者多于2个人则不能过桥.每个旅行者过桥都需要特定的时间, 两个旅行者同时过桥时时间应该算较慢的那个. 我们想知道所有旅行者最少要花费多少时间才能全部过桥? 想这题时,想到了正解一定是最快的两个人来回交替走,但是接下来思路就不清晰了; 我们考虑利用时间最小的两个人倒运,把时间大的人依次送过去 有两种方式: 1.时间最小的人和时间最大的人过去,然后时间最小的人把火把拿回来 2.时间最小和第二小的两个人过去,然后时间最小的人把火把拿回来;接着时间最大和第二大的两个人过去,时间第二小的人把火把拿回来 ``` signed main() { #ifdef M207 freopen("in.in","r",stdin); #endif in(n); for(ri i=1;i<=n;++i) in(a[i]); if(n<=2) { printf("%lld",a[n]); return 0; } for(ri i=n-1;i>=2;--i) { f[i]=f[i+1]+a[1]+a[i+1]; if(i+2<=n) f[i]=min(f[i],f[i+2]+a[2]+a[1]+a[i+2]+a[2]); } printf("%lld",f[2]+a[2]); return 0; } ``` ## POI2017 Prawnicy (pra) ~~仙呐,仙呐~~ 题意,,,懒得写了; 核心是:给定多个区间,每次询问一个区间,问它被多少个区间包含;而且询问区间的端点单调递增; 对于询问的左右区间遇到左右端点时分类讨论,统计贡献; 网上题解:对于一个左端点,选右端点前k大,用堆; ```cpp int count(int l,int r) { while(cl<l) { ++cl; for(ri i=0;i<st[cl].size();++i) if(st[cl][i].fi>=cr) { if(!ap[st[cl][i].se]) { ap[st[cl][i].se]=1; ++cnt; } } } while(cr<r) { for(ri i=0;i<en[cr].size();++i) if(ap[en[cr][i].se]) { ap[en[cr][i].se]=0; --cnt; } ++cr; for(ri i=0;i<en[cr].size();++i) if(en[cr][i].fi<=cl) { if(!ap[en[cr][i].se]) { ap[en[cr][i].se]=1; ++cnt; } } } return cnt; } ```