zoukankan      html  css  js  c++  java
  • 【UOJ#76】【UR #6】懒癌(动态规划)

    【UOJ#76】【UR #6】懒癌(动态规划)

    题面

    UOJ

    题解

    神....神仙题。
    先考虑如果是完全图怎么做。。。
    因为是完全图,所以是对称的,所以我们只考虑一个有懒癌的人的心路历程。
    如果只有一只狗有懒癌:第一天,看了看,似乎其他的狗都没有,但是村子里至少有一只狗有,然后就确定了。
    如果有两只狗:第一天,看了看,有一只别的狗有懒癌,不确定;第二天,昨天有懒癌的那只狗还活着,证明他不能确定,所以他还看到了别的狗有懒癌,而除了自己的未知和那个有懒癌的人,别的人的狗都没有懒癌,所以自己的狗有懒癌。
    那么递归下去,似乎可以得到如果有(k)只狗有懒癌,那么在完全图的情况下就会在第(k)天同时开枪。

    那么不难发现一切都是在这个人假装自己的狗没有病的前提下,如果他按时听到了枪声,那么他会认为自己的狗没病,否则没有按时听到枪声,他就会认为自己狗病了。那么这个枪声的时间是什么时候呢?显然这个人能够确定的是他所有能够看到的人的狗是否生病,其他的人任意情况都是可能的,那么设(f[S])表示生病状态是开枪时间的最小值,那么这个人会枚举所有的情况,如果在(max{f[T]})没有听到枪声,他就会在(max{f[T]}+1)时刻开枪。这样子直接暴力(dp)的复杂度大概是(O(4^nn))左右?
    这里打表可以发现当(Usubset V)时,(f[U]le f[V])。这样子就只需要把所有不能确定的全部默认为得了懒癌的,也就是全部看成(1)进行转移。
    复杂度可以优化到(O(2^nn))

    现在把时间给分开,一种是在有限时间内会开枪的,另外一个种是不会开枪的。考虑一下什么时候不会开枪,也就是一直无法确定的时候,最简单的例子就是只有两个人,他们互相看不见对方,那么永远都不知道时间。考虑构建出补图,即一个人如果看不到另外一个人就连一条边。那么一个不会开枪的情况在补图上表现为一个点数超过(1)的强连通分量。这是因为这些人之间的信息无法互相传递,导致信息不完整,所以永远都不会知道。
    那么我们可以把这些(SCC)给删掉,剩下的部分显然就是一个(DAG)
    我们考虑把有懒癌的点给染黑,没有的染白。我们把上面的那个(dp)的模型给往这里靠。
    这里的过程是:每次可以把一个黑点染白,然后把所有出边染黑。等到所有点都变白了之后,曾经被染黑过的点的个数就是答案。
    那么我们回到上面的(dp),类比一下这个过程,每次找到一个黑点,一开始默认自己的白的,然后把所有看不到的给变成(1),这个过程就是上面的(dp)转移。而每次都是在上次的基础上(+1)。那么对于每个黑点都要转移出去一次,所以答案和上面的(DAG)上的模型一样。

    那么我们把题目转化过来了,不难发现这个答案就是黑点点集能够访问到的点的个数。对于第一问考虑答案,显然每个点单独考虑,假设其能够到达这个点的点的个数为(r),那么贡献就是((2^r-1)*2^{n-r})
    对于第二问而言,显然只有不存在别的黑点能够到达这个黑点的时候才会被统计,所以答案就是(2^{n-r})
    那么拿(bitset)统计就行了。

    #include<iostream>
    #include<cstdio>
    #include<bitset>
    using namespace std;
    #define MOD 998244353
    #define MAX 3030
    int n,dg[MAX],bin[MAX],S[MAX],N;
    bitset<MAX> a[MAX],g[MAX];
    char s[MAX];
    int main()
    {
    	scanf("%d",&n);
    	bin[0]=1;for(int i=1;i<=n;++i)bin[i]=(bin[i-1]*2)%MOD;
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%s",s+1);
    		for(int j=1;j<=n;++j)
    			if(i^j)g[i][j]=s[j]=='0',dg[i]+=g[i][j];
    	}
    	for(int i=1;i<=n;++i)if(!dg[i])S[++N]=i;
    	for(int i=1;i<=N;++i)
    		for(int j=1;j<=n;++j)
    			if(g[j][S[i]]&&!--dg[j])S[++N]=j;
    	for(int i=1;i<=N;++i)a[S[i]][S[i]]=1;
    	for(int i=N;i;--i)
    		for(int j=1;j<=n;++j)
    			if(g[S[i]][j])a[j]|=a[S[i]];
    	int ans1=0,ans2=0,r;
    	for(int i=1;i<=N;++i)
    		r=N-a[S[i]].count(),ans1=(ans1+1ll*(bin[N-r]-1)*bin[r])%MOD,ans2=(ans2+bin[r])%MOD;
    	printf("%d %d
    ",ans1,ans2);
    	return 0;
    }
    
  • 相关阅读:
    C/C++定义全局变量/常量几种方法的区别
    可变参数宏__VA_ARGS__
    mysql 命令重命名表RENAME TABLE 句法
    贝尔实验室的历史
    SVN代码回滚
    linux下查看进程占用端口和端口占用进程命令
    php操作mongodb中的ISODate格式日期
    Vim多行缩进技巧
    关于XCode工程中PrefixHead.pch文件的使用
    Object C函数指针@selector
  • 原文地址:https://www.cnblogs.com/cjyyb/p/11093389.html
Copyright © 2011-2022 走看看