CodeForces 1045B Space Isaac(推导 + Manacher)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/83241870

 

这题居然是字符串题!居然是字符串题!字符串题!

大致题意:总共有M个物品,编号从0~M-1。总共有两个背包,告诉你第一个背包有N个物品,编号分别是a1,a2,...,an。剩下的物品都在第二个背包。现在从第一个背包里面拿出任意一个物品,其编号是x,从第二个背包里面拿出一个物品,其编号是y,计算出z=(x+y)%M。我们知道总共有N*(M-N)种取法,考虑完这么多种取法后,z始终不能取到哪些数值,把值的数目以及每个数值输出。

首先,显然这些数字的范围我么可以缩小到N*N个以内,也即z不能够取到的值,只有可能是一号背包里面任意两个物品编号之和。但是我们发现,满足这个条件并不能说明这个数字就一定不能够取到。那么满足条件的什么数字还是可以被z取到呢?我们发现,如果存在(ai+aj)%M=(ak+x)%M且x不属于第一个背包,那么这个(ai+aj)%M就是不合法的。也就说,对于一个合法的和sum,对于任意的ai,都会存在一个aj,使得(ai+aj)%M=sum。如果我们对ai排序,我们会发现如果满足条件,(a1+an)%M=(a2+an-1)%M=(a3+an-3)%M......由于是有取模的,所以这个序列不应该是环状的,也就是说这个对称中心是可以移动的,如果存在一个对称中心,这个对称的范围覆盖所以的N,那么这个中心对应的答案就是一个正确的答案。看到对称中心和对称范围,很容易想到这就是一个回文串,对应回文中心和回文半径。而如果把这个序列差分一下,我们发现,恰好就是回文串。

所以我们对第一个背包的物品编号排序后差分,扩大长度为两倍以表示环状结构,然后再进行Manacher求出每一个回文中心对应的最大的回文半径。如果第一个背包的物品数量是奇数个,那么这个回文中心一定得是物品,对应差分之后的序列中心一定是#,而且满足条件不仅是半径要大于N/2,而且要求对称两个数字之和在模M的意义下等于回文中心对应数字的两倍。如果是偶数,那么可以和奇数的时候一样,也可以对称中心不是数字。按照这样的方式统计即可。

最后,当N==1的时候需要特判,答案就是2*a1。具体见代码:

#include<bits/stdc++.h>
#define LL long long
#define mod 1000000007
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define INF 0x3f3f3f3f
#define sf(x) scanf("%d",&x)
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)

using namespace __gnu_pbds;
using namespace std;

const int N = 1e6 + 10;

set<int> ans;
int n,m,a[N],s[N],p[N];

void manacher()
{
    int len=2*n-1;
    for(int i=len;i>=0;--i)
        s[i+i+1]=s[i],s[i+i]=-1;
    int id,mx=0;
    for(int i=1;i<len+len+1;++i)
    {
        if (mx>i) p[i]=min(p[2*id-i],mx-i); else p[i]=1;
        while(s[i-p[i]]==s[i+p[i]]&&i-p[i]>=0&&i+p[i]<len+len+1)++p[i];
        if(i+p[i]>mx) id=i,mx=p[i]+i;
    }
}

int main()
{
    sf(n); sf(m);
    if (n==1)
    {
        int x; sf(x);
        printf("1\n%d",2*x%m);
        return 0;
    }
    for(int i=0;i<n;i++) sf(a[i]);
    for(int i=1;i<=n;i++)
        s[i-1]=(a[i%n]-a[i-1]+m)%m;
    for(int i=n;i<2*n-1;i++) s[i]=s[i-n];
    manacher();
    for(int i=2;i<4*n;i+=2)
        if (p[i]/2>=n/2&&(a[(i/2-1)%n]+a[(i/2+1)%n])%m==a[(i/2)%n]*2%m) ans.insert(2*a[(i/2)%n]%m);
    if (n%2==0)
        for(int i=1;i<4*n;i+=2)
        {
            if (p[i]>=n) ans.insert((a[(i/2)%n]+a[(i/2+1)%n])%m);
        }
    printf("%d\n",ans.size()); int t=0;
    for(auto i:ans)
    {
        t++;
        printf("%d%c",i," \n"[t==ans.size()]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/83241870