好难啊。。争取一题一题填吧。。
csdn没有代码缩起来的操作。。贴出来太丑了。。要代码的就自己去牛客拿吧。
通过观察可以发现,题目等价于从1开始按顺序把所有东西串起来,每个点只可以被串进来一次和出去一次,等价于很多个区间,互不相交。。判一下就好了
具体证明也不是很会。。
一开始互不相交写错了。。很久才反应过来。。但实际上有一个很好写的做法,就是可以观察到他是一个括号序列模型,用栈维护就好了
如果边权为1,就是简单的删边博弈
可以猜想
当边权不为1时,边权偶数,转移sg;边权大于1的奇数,转移sg^1
通过和论文同样的方法可以证明
然后就可以直接做了
输出方案也比较简单,做多一次dfs即可,这里不做详细说明了
先考虑,如果给你一个x,y你怎么把答案算出来
很容易发现, − 1 -1 −1操作只会至多做一次,然后就可以 O ( n ) O(n) O(n)做了
但是如果只观察到这一个结论是不够的
我们要继续分析一下这个问题
从高位往低位来看
如果,对于一个位,x和y是是一样的,那么他们不作出贡献
当我们找到最后一个位i,满足 x i > y i x_i>y_i xi>yi,且他在最前的一个位j,满足 x j < y j x_j<y_j xj<yj的前面
那么你会发现,决策就只有一种,就是把 [ i + 1 , n ] [i+1,n] [i+1,n]全部变为0,然后进行 − 1 -1 −1操作,然后剩下就是直接匹配的就OK了
讨论一下各种情况,可以发现,答案等价于,x里面1的个数减去y里面1的个数再加上 ( n − i ) (n-i) (n−i)
想到这里,就可以直接数位DP了,过程比较简单,这里就不详细说明了
成为了第三个不一样的AC代码,感觉很棒棒啊
首先,不难想到我们可以根据有没有出现过完整的周期来做
一开始的写法设了一个阈值,为 n / 2 n/2 n/2, > n / 2 >n/2 >n/2按只出现一次的写, < = n / 2 <=n/2 <=n/2的按有周期的写。但是后来发现,有可能出现 w w w在中间,然后头尾都有 ∣ w ∣ − 1 |w|-1 ∣w∣−1的长度,那么就GG了。。忘了为什么这么想。。可能是因为这样好讨论(大雾
由于过程有点复杂,所以还是对着代码讲吧。。
fix了一下,发现这个阈值是没有用的。。我们还是只讨论有没有完整周期就可以了
首先,你要跑一个 E X K M P EXKMP EXKMP和 m a n a c h e r manacher manacher,对于EXKMP,我们只需要知道,某一位能不能拉到尾,我的代码里记为 o k i ok_i oki,对于 m a n a c h e r manacher manacher,我们要记录 i i i作为反串的开头,也就是反过来匹配,可以匹配多远,记为 f i f_i fi,顺便记录一个一个串能不能到头和到尾,记为 o k 1 i ok1_i ok1i和 o k 2 i ok2_i ok2i
对于没有周期的, ( i , j ) (i,j) (i,j)合法的条件是
int t=i-u+1;
if (i<=t&&j+1>n-t+1&&ok2[j+1]&&ok1[i]) ans++;
然后可以发现,只要枚举i,j是一个区间,等同于求这个区间里面 o k 2 = 1 ok_2=1 ok2=1的个数,前缀和即可
这个部分的复杂度是 O ( n ) O(n) O(n)的
然后是有周期的,我们就可以找周期来做
稍微观察可以发现,周期的情况,我们只需要枚举余数来找即可,别的都可以计算
自己画画图。。可以发现,对于每一个合法的周期长度 i i i,那么他的 o k i + 1 ok_{i+1} oki+1一定是 t r u e true true的,那么就可以得到可能的合法的长度了
至于合法的长度,其实就是他的回文比所需长度大即可
写成代码大概是这样的(其中u是周期长度+1,t是u/2):
for (int i=u-t;i<=u-1;i++)//枚举终点哪一个东西在哪里
if (f[i]>=t) ans=ans+(n-i+1)/t;
容易发现,我们可以发现,t是递增的,并且每次加上的数最多只有两个取值,把两个取值分开算就好了
于是可以把 f i f_i fi排序后,就是区间和了,树状数组维护即可
这个部分的复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)的
然后就可以通过此题了
如果要完整代码可以自己去牛客拿
考虑,如果不是环的话,就是个傻逼题。。然而这个并没什么用
对于环的情况,我们和不是环一样正着做,那么就只是多了一个限制,就是我们想知道最后一个set和第一个最少要有多少个一样的
考虑二分答案,后进行DP
f i f_i fi第 i i i个set和第1个set相同的下界,g表示上界
假设1号set是 [ 1 , m ] [1,m] [1,m]
如果我们想要上界,那么肯定是要上一个取上界,然后这一个从x开始取
如果我们想要下界,那么肯定是下一个取下界,然后这个从1开始取
分类讨论以后不难得到转移(其中x是二分的答案):
一开始用系STLmin居然T了。。然后写了一个手写卡常才过。。
if (m-f[u-1]>=a[u]) g[u]=m;
else g[u]=(m-f[u-1])+(m-a[u]);
LL r=m-g[u-1]+m,now=x-r;
f[u]=m-_min(m,now+_min(m-a[u],r-m));
DP完之后随意判断一下就好了
其实像这个类型的题,你可以设定某一个值,经过lim个加速点
如果答案经过的比lim要大,那么你就可以认定 B u r s t ! Burst! Burst!了
并且认定答案一定不会改变,就像样例1一样
为了防止炸精度,可以一开始给所有数乘上一个 2 l i m 2^{lim} 2lim
然后你就可以DP了,DP的话,按照经过加速点的个数来转移感觉这个不是重点了。。
经过实验,发现,如果lim取大概59,用long long来存,你可以过大概12.5%的数据
看别人的代码,别人用的都是 i n t 128 int128 int128,这样lim就可以取到110左右了
然后就可以通过此题了。。
这题做了挺久。。分数的变化过程为 45 − 65 − 85 − 100 45-65-85-100 45−65−85−100
其实一开始的100还是有点bug的。。然后又改了一点。。不知道还有没有bug。。反正已经AC了。。就不管了
这题说白了其实就是一个分类大讨论
分0的个数最多,1的个数最多,2的个数最多来讨论
我这里举一个0的个数的栗子,别的自己去讨论吧
假设个数分别为a,b,c,最后一个 a a a出现的位置为 l a la la
这里分三个情况 b + c = a b+c=a b+c=a,那么我们只需要从 l a la la一直取到 2 2 2就可以了
如果 b + c = a − 1 b+c=a-1 b+c=a−1,那么我们通过上述取法以后,还需要吧 [ 1 , 1 ] [1,1] [1,1]取走
否则,容易发现,这个取法是不行的,我们至少要有一对 [ 2 , 3 ] [2,3] [2,3]凑在一起
通过计算发现,凑在一起的个数是 t = ( b + c − ( a − 1 ) ) / 2 t=(b+c-(a-1))/2 t=(b+c−(a−1))/2
那么就从 l b lb lb开始往前取 t t t个
然后用同样方法取就可以了
要注意的点是,移的顺序,不要说两个阶段移过去不合法
比如说,对于 2 2 2最多的情况,你先移了 01 01 01,后来移了一个 12 12 12就不合法了
剩下的情况就自己推导吧。。我觉得2和0的情况是差不多的,1的情况稍微复杂一点。。但是数据不是很强。。讨论地不是很好也可以AC