• 6423. 【NOIP2019模拟11.11】画


    题目描述

    Description

    Input

    Output

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

    Sample Output
    15

    Data Constraint

    题解

    迫真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 <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #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<b.s;
    }
    
    void New(int x,int y)
    {
    	++len;
    	a[len][0]=y;
    	a[len][1]=ls[x];
    	ls[x]=len;
    }
    
    long long qpower(long long a,int b)
    {
    	long long ans=1;
    	
    	while (b)
    	{
    		if (b&1)
    		ans=ans*a%mod;
    		
    		a=a*a%mod;
    		b>>=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 (I<d[j][i])
    							add(G[1][l^I],g[k][l]*P[i]);
    							else
    							{
    								if (i)
    								add(G[k][l^I],g[k][l]*D[j][i-1]);
    								else
    								add(G[k][l^I],g[k][l]);
    							}
    						}
    					}
    				}
    				
    				fo(k,0,1)
    				{
    					fo(l,0,1)
    					g[k][l]=G[k][l],G[k][l]=0;
    				}
    			}
    			
    			add(f[s],g[1][c[i]]*P2[i]);
    			
    			bz=0;
    			fo(j,1,n)
    			if (p[j-1]&s)
    			bz^=d[j][i];
    			
    			if (bz!=c[i])
    			break;
    		}
    		
    		S=0;
    		fo(i,1,n)
    		if (s&p[i-1])
    		S^=lim[i];
    		
    		f[s]+=S==C;
    	}
    }
    
    void work1_5()
    {
    	int i,j,k,s;
    	
    	fo(s,1,L)
    	{
    		l=low(s);
    		
    		k=0;
    		fo(i,1,n)
    		if (s&p[i-1])
    		{
    			for (j=ls[i]; j; j=a[j][1])
    			if (i<a[j][0] && s&p[a[j][0]-1])
    			{
    				++k;
    				break;
    			}
    		}
    		G[s]=(k==0);
    	}
    }
    
    void work2()
    {
    	int s,s1;
    	
    	fo(s,1,L)
    	{
    		l=low(s);
    		g[s]=G[s];
    		
    		for (s1=(s-1)&s; s1; s1=(s1-1)&s)
    		if (s1&l)
    		add(g[s],-g[s1]*G[s^s1]);
    	}
    }
    
    void work2_5()
    {
    	int i,s,bz;
    	
    	fo(s,1,L)
    	{
    		bz=-1;
    		
    		fo(i,0,n-1)
    		if (s&p[i])
    		{
    			++tot[s];
    			
    			if (bz==-1)
    			h[s]+=p3[i],bz=i;
    			else
    			h[s]+=p3[i]*2;
    		}
    		
    		if (!(tot[s]&1))
    		{
    			h[s]+=p3[bz];
    			H[s]=(lim[bz+1]+1)%mod;
    		}
    	}
    }
    
    void work3(int t,int s1,int s2,int s3) //s2:1 s3:0
    {
    	int l,S,s;
    	
    	if (t>n)
    	{
    		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
    ",(ans+mod)%mod);
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    
  • 相关阅读:
    小笔记系列——Excel中获取当前日期
    Git 错误:OpenSSL SSL_read: Connection was reset, errno 10054
    cmd_切换文件目录的几种方法
    Jupyter Notebook 常用操作(持续更新中……)
    chrome 浏览器书签保存
    各种开发工具注释的快捷键(持续更新中…)
    Spyder 快捷键(注释、跳转、缩进)
    ISlide插件安装后,PPT无法正常关闭
    [TimLinux] 操作系统实战45讲
    [TimLinux] vnc and go bashrc
  • 原文地址:https://www.cnblogs.com/gmh77/p/11845460.html
Copyright © 2020-2023  润新知