UR 6 懒癌

神题(⊙o⊙)…

   SOL:

     先胡一波自己的看法:

         有三个引理: 

           1.只会在一天内死狗,不会有不同的两天都死了狗。因为死狗是基于一个事实:你们中至少有一条狗有病。那么一天死狗后这个事实就不一定成立了。

           2.死狗的情况至少要有一个人能看见剩下所有人。我们称之为重要人物。

           3.我们可以每次把全部的重要人物都拿出来,形成一张子图和一个重要集合。那么要么重要集合中没有有病的狗,要么重要集合中的狗死。

          4.若重要集合中没有病狗,我们可以递归解决这个子图,直到子图中没有重要人物。

       唔,这个做法的唯一的毛病就是转移递归,因为递归的条件是一层的重要人物互相确认自己这个集合中没有病狗并且被下一层的重要人物知道。但下一层的重要人物的信息其实是不对称的,所以有些人会先知道,有些人后知道,先知道的人会开启一个子博弈,这样的话就不好同时统计2^n种情况,这样写的复杂度应该就在O(2^n* 一个多项式)内。显然没有标算优美。

     标算是说一个人不会在怀疑自己家狗有没有病的时候就把其杀的。故一个人总是假设自家狗没病,而确定自家狗有病是在排除所有假设自家狗没病的情况下的天数+1。

    考虑状压DP,令dpU表示U集合中的狗生病时的开枪时间。然后我们接着靠着伏特跳蚤国王超高逼格的智商分析:如果我的狗没病,那么什么时候应该听到枪声呢?于是跳蚤国王在大脑中枚举了所有可能的生病情况V,那么如果他的狗没有生病,他最迟应该在maxdpVmaxdpV的时间听到枪声。所以他会在第maxdpV+1天开枪。

    然后对于生病状态UV,如果UV,那么必然有dpUdpV

    题解里对这个结论的证明其实是有问题的,因为题解先按照这个结论构造图,再在这张图上说明问题。

    但这个结论显然是对的,个人认为没什么好证的。

   考虑根据转移建一个新图,如果ii不能看到jj,那么就在ii到jj连一条有向边。这个新图中可能有若干个强连通分量,

   如果一个生病状态中有生病的狗的主人是在一个多于一个点的强连通分量中,那么这一个状态一定无法在有限时间内开枪(转移有环)

   ,否则一定能在有限时间内开枪(转移无环)。

    我们把环删掉,得到一张DAG就可以跑我开头说的那个算法了

     所以问题又变成了:

      一个DAG上有一些点被染黑了,接着每一时刻,我们可以把一个黑点染白,然后把这个点连向的点全部染黑。若干轮之后,求一个黑点能染黑几个点。(这就是死了几条狗)。

    我们在考虑一个点不变成这样一个黑点的情况,统计这个点被记入这个时间的贡献,这就好了。

    

#include<bits/stdc++.h>
#define N 3007
#define mo 998244353
#define LL long long
#define eho(x) for(int i=1;i<=n;i++)
using namespace std;
bitset<N> Q[N];
LL qsm(LL x,LL y){
    static LL anw;
    for (anw=1;y;y>>=1,x=x*x%mo) if (y&1) anw=anw*x%mo;
    return anw;
}
LL ans1,ans2,anw;
int p[N][N],dre[N],q[N],be,ed,x,n;
char ch[N];
signed main () {    scanf("%d",&n);
    for (int i=1;i<=n;i++) {
        scanf("%s",ch+1);
        for (int j=1;j<=n;j++)  
         p[i][j]=ch[j]=='0'&&i!=j,dre[i]+=p[i][j];
    }
    for (int i=1;i<=n;i++) if (!dre[i]) q[++ed]=i;
    while (be<ed) {
        x=q[++be];
        eho(x)  { 
          dre[i]-=p[i][x]; 
           if (!dre[i]&&p[i][x]) q[++ed]=i;}
    }
    for (int id=ed;id;id--) {
        x=q[id];
        Q[x].set(x);
        eho(x) if (p[x][i]) Q[i]|=Q[x];
        anw=Q[x].count();
        cerr<<anw<<endl;
        ans1+=(qsm(2,anw)-1)*qsm(2,ed-anw)%mo;
        ans2+=qsm(2,ed-anw);
    } 
    return cout<<(ans1%mo)<<' '<<(ans2%mo)<<endl,0;
}

      

原文地址:https://www.cnblogs.com/rrsb/p/8885863.html