• [GYM102832J]Abstract Painting


    CXXVI.[GYM102832J]Abstract Painting

    考虑将一个圆心为 \((x,0)\),半径为 \(R\) 的圆,转换为 \(x\) 轴上线段 \([x-R,x+R]\),问题转换为求无交的线段覆盖方案数。

    因为所有的圆半径很小(\(5\)),所以我们考虑状压位置 \(i\) 前面 \(10\) 位的信息(在实际应用中会发现只要状压 \(9\) 位即可,因为第 \(i-1\) 位必然为 \(0\)),表示第 \(i\) 位能否作为一个圆的左端点。当我们在位置 \(i\) 加入一个圆 \([l,i]\) 时,需要保证当前状压的状态中第 \(l\) 位为 \(0\)\(l\geq0\)\(i-l\)\(\leq10\) 的偶数。在加入这么一个圆后,第 \(l+1\sim i-1\) 位都不能作为圆的左端点,更新状态即可。同时,因为一个位置可以作为不止一个圆的右端点,所以还得枚举作为哪些圆的右端点。因为所有可能的直径只有 \(2,4,6,8,10\),所以直接 \(2^5\) 枚举即可。记得判断加入的圆的集合是否包含了必须加入的圆的集合!

    总复杂度 \(n\times2^9\times2^5\),可以通过。

    (附,通过子集枚举等trick可以将复杂度优化到 \(3^5\times 2^5\),但二者实际效率只不过差了个大约 \(2\) 的常数,而且不加也能过,就不加了)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    int n,m,mus[1010],f[1010][1<<9],all=(1<<0)|(1<<2)|(1<<4)|(1<<6)|(1<<8),res;
    int fob(int ip){
    	int lim=1;
    	while(lim<=ip)lim<<=1;
    	return lim-1;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),mus[x+y]|=(1<<((y-1)<<1));
    	f[0][0]=1;
    	for(int i=1;i<=n;i++)for(int j=all;;j=(j-1)&all){
    		if((j&mus[i])==mus[i]&&j<(1<<min(i-1,9))){
    			int J=fob(j);
    //			printf("%d:%d:%d\n",i,j,J);
    			for(int k=0;k<(1<<min(i-1,9));k++){
    				if(k&j)continue;
    				(f[i][((k<<1)&((1<<9)-1))|J]+=f[i-1][k])%=mod;
    			}			
    		}
    		if(!j)break;
    	}
    	for(int i=0;i<(1<<9);i++)(res+=f[n][i])%=mod;
    	printf("%d\n",res);
    	return 0;
    } 
    

  • 相关阅读:
    图的深度遍历
    数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历

    满汉全席
    2-sat(模板)
    2-sat
    花匠
    维护序列NOI2005
    序列终结者
    杨辉三角
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601465.html
Copyright © 2020-2023  润新知