• BZOJ 2669 Luogu P3160 [CQOI2012]局部极小值 (容斥原理、DP)


    题目链接

    (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2669
    (luogu) https://www.luogu.org/problem/P3160

    题解

    这道题充分暴露了我的菜。。

    显然两个局部极小值点不能相邻,所以最多有(8)个局部极小值。
    然后考虑容斥掉.不能成为局部极小值的限制,那么就变成钦定某些位置一定是局部极小值,其余不限,求方案数。

    然后这个可以状压DP,考虑从小到大加入每个数,然后就很好求了。

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cassert>
    #include<iostream>
    #include<vector>
    using namespace std;
    
    const int P = 12345678;
    const int dx[8] = {1,0,-1,0,1,1,-1,-1},dy[8] = {0,1,0,-1,1,-1,-1,1};
    vector<int> kx,ky;
    int bitcnt[(1<<8)+3];
    char a[7][11];
    char b[7][11];
    int num[(1<<8)+3];
    int dp[31][(1<<8)+3];
    int n,m,cnt,ans;
    
    bool check(int x,int y,int typ)
    {
    	bool ret = true;
    	for(int i=0; i<8; i++)
    	{
    		int xx = x+dx[i],yy = y+dy[i];
    		if(xx>0&&xx<=n&&yy>0&&yy<=m)
    		{
    			if(typ==0) {if(a[xx][yy]=='X') {return false;}}
    			if(typ==1) {if(b[xx][yy]=='X') {return false;}}
    		}
    	}
    	return true;
    }
    
    int calc()
    {
    	kx.clear(); ky.clear();
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=1; j<=m; j++)
    		{
    			if(a[i][j]=='X') {kx.push_back(i); ky.push_back(j);}
    		}
    	}
    	for(int i=0; i<(1<<kx.size()); i++)
    	{
    		for(int j=0; j<kx.size(); j++)
    		{
    			if(!(i&(1<<j))) {b[kx[j]][ky[j]] = 'X';}
    		}
    		num[i] = 0;
    		for(int j=1; j<=n; j++)
    		{
    			for(int k=1; k<=m; k++)
    			{
    				bool ok = check(j,k,1);
    				if((ok && a[j][k]!='X')) num[i]++;
    			}
    		}
    		num[i] += bitcnt[i];
    		for(int j=0; j<kx.size(); j++) {b[kx[j]][ky[j]] = '.';}
    	}
    	dp[0][0] = 1;
    	for(int i=0; i<n*m; i++)
    	{
    		for(int j=0; j<(1<<kx.size()); j++)
    		{
    			if(dp[i][j])
    			{
    				dp[i+1][j] = (dp[i+1][j]+dp[i][j]*(num[j]-i))%P;
    				for(int k=0; k<kx.size(); k++)
    				{
    					if(!(j&(1<<k)))
    					{
    						dp[i+1][j|(1<<k)] = (dp[i+1][j|(1<<k)]+dp[i][j])%P;
    					}
    				}
    			}
    		}
    	}
    	int ret = dp[n*m][(1<<kx.size())-1];
    	for(int i=0; i<=n*m; i++) for(int j=0; j<(1<<kx.size()); j++) dp[i][j] = 0;
    	return ret;
    }
    
    void dfs(int x,int y,int dep)
    {
    	if(x==n+1)
    	{
    		int tmp = calc();
    		if((dep-cnt)&1) {ans = ans-tmp<0 ? ans-tmp+P : ans-tmp;}
    		else {ans = ans+tmp>=P ? ans+tmp-P : ans+tmp;}
    		return;
    	}
    	int xx = x,yy = y+1; if(yy>m) {yy = 1; xx++;}
    	if(a[x][y]=='X')
    	{
    		bool f = check(x,y,0);
    		if(f) {dfs(xx,yy,dep+1);}
    	}
    	else
    	{
    		a[x][y] = 'X';
    		bool f = check(x,y,0);
    		if(f) {dfs(xx,yy,dep+1);}
    		a[x][y] = '.';
    		dfs(xx,yy,dep);
    	}
    }
    
    int main()
    {
    	for(int i=1; i<(1<<8); i++) bitcnt[i] = bitcnt[i>>1]+(i&1);
    	scanf("%d%d",&n,&m);
    	for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) b[i][j] = '.';
    	for(int i=1; i<=n; i++) scanf("%s",a[i]+1);
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=1; j<=m; j++) {if(a[i][j]=='X') cnt++;}
    	}
    	if(cnt>8) {printf("0"); return 0;}
    	dfs(1,1,0);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    设计模式怎样解决设计问题
    抽象、多样性与可变性
    框架最符合开闭原则
    PHP开发api接口安全验证
    HTTP API接口安全设计
    MySQL——修改root密码的4种方法(以windows为例)
    关于nginx中不用.htaccess 用在ningx.conf中配置的问题
    API接口安全性设计
    Linux上vi(vim)编辑器使用教程
    LNMP下FTP服务器的安装和使用(Pureftpd和Proftpd)
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11268587.html
Copyright © 2020-2023  润新知