6423. 【NOIP2019模拟11.11】画

题目描述

Description
6423. 【NOIP2019模拟11.11】画_第1张图片

Input
6423. 【NOIP2019模拟11.11】画_第2张图片

Output

Sample Input
3 2 3
3 6 5
1 2
1 3

Sample Output
15

Data Constraint
6423. 【NOIP2019模拟11.11】画_第3张图片

题解

迫真CSP模拟

简单容斥(×)

容斥套dp套容斥套dp(√)


先把lim按从小到大排序,同时把边的编号也改过来

考虑没有边时怎么做

枚举一个数位i,假设在i之前的n个数都等于lim,并且要保证i以前的异或和等于C的对应位置

如果i这一位上有一些数没有等于lim,那么先把一个没有等于lim的数x提出来,然后再填其他的数

其他的数在不超过限制(或在第i位已经小于lim)的情况下随便填,x必然有唯一一种填法使得答案为C

所以可以直接算,把小于lim的乘上2^i,等于lim的乘上后面的那一段

最后除以2^i后加进去

要考虑全部等于lim的情况和C=0时空集的答案为1

这一部分是O(2^n*n*64)的


接着考虑有边的情况

对于一条边,将其容斥成 无限制-相等,最后变成至少为0-至少为1+至少为2...的形式

简单证明(二项式反演的套路):

对于有m(m>=0)条边相等时,一种方案被算的次数

\(ans=\sum_{i=0}^{m}{(-1)^i(_i^m)}\)

\(=\sum_{i=0}^{m}{(-1)^i(_i^m)*1^{m-i}}\)

\(=\sum_{i=0}^{m}{(-1)^i(_i^m)*1^{m-i}}\)

\(=(-1+1)^m\)

\(=[m=0]\)

显然只有m=0时的方案才会被算到


容斥完之后,可以发现变成了若干连通块,每个连通块内的值都相等

考虑计算每个连通块的容斥系数之和

由于要保证连通,所以可以用 所有方案-不连通 来算

对于m条边的所有方案容斥系数之和:

\(=\sum_{i=0}^{m}{(-1)^i(^m_i)}\)

然后就和上面一样了

(但是实际上这个式子和上面的含义是不同的,上面的可能有>m条边)

所以所有方案就是[边数=0],不连通的可以先枚举最小的点所在块,然后乘以剩下的随便选的方案

要预处理每个点集中随便选的方案,时间为O(2^n*n^2)

(但是只要有一条边就可以退了,所以跑不满)

处理容斥的时间复杂度为O(3^n)


处理完容斥系数和答案后,考虑把它们合起来,每次加上当前剩余未加的最小点所在块(不会算重)

对于一种情况,假设已经知道了容斥系数和每个块的大小和块中最小的lim

那么对于大小为偶数的块,显然异或后影响为0,所以方案乘上(最小的lim+1)

对于大小为奇数的块,异或后会剩下一个0~lim

全部n个数做完之后可能会剩下若干个数,此时就相当于没有限制的情况了

设一个三进制状态,表示每个数没选/选了且是剩下的/选的但不是剩下的(0/1/2)

递归转移,每次存 当前的三进制状态、有那些位是最小的、有哪些位还没选(后面两个是二进制)

这样避免了直接枚举后拆状态的O(n)

转移就在没选的位上找子集,如果选满了就乘上剩下的贡献后加到答案里

仍要预处理出每个二进制子集的大小和对应的三进制(如果子集大小为奇数则最小的为1其余为2,否则全为2)

预处理的时间为O(2^n*n)

最终dp的时间:由于每次选的是最小点,设当前的最小点为i

那么时间为\(\sum_{i=1}^{n}{2^{i-1}*3^{n-i}}\)

因为前i-1位只能是1或2,后n-i位只能是0或2,所以枚举前面的就是\(2^{i-1}\),枚举i+1~n相当于一个(n-i)大小的子集转移问题

求和一下大约等于O(3^n)

然而直接写会挂,因为直接枚举的时间是O(4^n)

其实只要dp不为0时再转移就可以做到O(3^n)了

code

#include 
#include 
#include 
#include 
#include 
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define add(a,b) a=((a)+(b))%998244353
#define low(x) (x&-(x))
#define mod 998244353
#define Mod 998244351
using namespace std;

struct type{
    long long s;
    int id;
} b[16];
bool c[60];
long long p[60];
long long P[60];
long long P2[60];
int p3[15];
int a[301][2];
int ls[16];
int tot[32768];
int h[32768];
long long H[32768];
long long f[32768];
long long G[32768];
long long g[32768];
long long dp[14348907];
long long lim[16];
bool d[16][60];
long long D[16][60];
int num[16];
int L,n,m,i,j,k,l,len,Tot;
long long C,ans;

bool cmp(type a,type b)
{
    return a.s>=1;
    }
    
    return ans;
}

void work1()
{
    long long g[2][2];
    long long G[2][2];
    long long S;
    int i,j,k,l,s,I;
    bool bz;
    
    memset(g,0,sizeof(g));
    memset(G,0,sizeof(G));
    
    f[0]=C==0;
    fo(s,1,L)
    {
        fd(i,59,0)
        {
            g[0][0]=1;g[0][1]=0;
            g[1][0]=0;g[1][1]=0;
            
            fo(j,1,n)
            if (p[j-1]&s)
            {
                fo(k,0,1)
                {
                    fo(l,0,1)
                    {
                        fo(I,0,d[j][i])
                        {
                            if (In)
    {
        if (dp[s1])
        {
            if (s3)
            {
                l=low(s3);
                S=s3^l;
                
                for (register int s4=S; s4; s4=(s4-1)&S,++Tot)
                {
                    s=s4^l;
                    
                    if (tot[s]&1)
                    add(dp[s1+h[s]],dp[s1]*g[s]);
                    else
                    add(dp[s1+h[s]],dp[s1]*g[s]%mod*H[s]);
                }
                if (tot[l]&1)
                add(dp[s1+h[l]],dp[s1]*g[l]);
                else
                add(dp[s1+h[l]],dp[s1]*g[l]%mod*H[l]);
            }
            else
            add(ans,dp[s1]*f[s2]);
        }
        
        return;
    }
    
    work3(t+1,s1,s2,s3+p[t-1]);        //0
    work3(t+1,s1+p3[t-1],s2+p[t-1],s3);//1
    work3(t+1,s1+2*p3[t-1],s2,s3);     //2
}

int main()
{
    freopen("draw.in","r",stdin);
    freopen("draw.out","w",stdout);
    
    p[0]=P[0]=P2[i]=1;p3[0]=1;
    fo(i,1,59)
    {
        p[i]=p[i-1]*2;
        if (i<=14)
        p3[i]=p3[i-1]*3;
        
        P[i]=p[i]%mod;
        P2[i]=qpower(P[i],Mod);
    }
    
    scanf("%d%d%lld",&n,&m,&C);L=p[n]-1;
    fo(i,1,n)
    scanf("%lld",&lim[i]),b[i]={lim[i],i};
    
    fo(i,0,59)
    c[i]=(C&p[i])>0;
    
    sort(b+1,b+n+1,cmp);
    fo(i,1,n)
    {
        num[b[i].id]=i;
        lim[i]=b[i].s;
    }
    
    fo(i,1,n)
    {
        fo(j,0,59)
        {
            d[i][j]=(lim[i]&p[j])>0;
            
            if (j)
            D[i][j]=D[i][j-1];
            else
            D[i][j]=1;
            add(D[i][j],d[i][j]*p[j]);
        }
    }
    
    fo(i,1,m)
    {
        scanf("%d%d",&j,&k);
        j=num[j],k=num[k];
        
        New(j,k);
        New(k,j);
    }
    
    work1();
    work1_5();
    work2();
    work2_5();
    dp[0]=1;
    work3(1,0,0,0);
    
    printf("%lld\n",(ans+mod)%mod);
    
    fclose(stdin);
    fclose(stdout);
    
    return 0;
}

你可能感兴趣的:(6423. 【NOIP2019模拟11.11】画)