HZOJ 巨神兵
阅读原文时间:2023年07月10日阅读:1

60pts:

每个DAG的拓扑序是唯一的,所以考虑将DAG分层。f[i][j]记录当前选择的节点状态是i,最后一层的节点状态为j(dep取最大)。

初始状态:$f[i][i]=1;i\in [1,1<<n)$。那么我们第一层枚举当前状态i,第二层枚举[1,1<<n)。那么令s=i&j,t=j&(~i),s即为i的一个子集,所以令s为当前的最后一层,t为i

的补集的一个子集,令t为转移后的最后一层,要求s到t中每个点都有边。枚举t中每个点,设ch1为集合$i-s$当前点的边数,ch2为s集合到当前点的边数,转移方程:$f[i$|$t][t]+=f[i][s]*\prod 2^{ch1}*\prod (2^{ch2}-1)$;

#include
#include
#include
#include
#include
#define LL long long
using namespace std;
const int mod=1e9+;
struct edge
{
int u,v,nxt;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].nxt
}ed[];
int first[],num_e;
#define f(x) first[x]
int n,m,ru[];
LL f[<<][<<]; vector fr[];
bool pd(int s,int t)
{
bool ok=;
for(int i=;i<=n;i++) if((<>n>>m;int tu,tv;
for(int i=;i<=m;i++)cin>>tu>>tv,add(tu,tv),ru[tv]++,fr[tv].push_back(tu);

 for(int i=;i<(<<n);i++)f\[i\]\[i\]=;  
 for(int i=;i<(<<n);i++)  
 {  
     for(int j=;j<(<<n);j++)  
     {  
         int s=i&j,t=j&(~i);  
         bitset<>t1(i),t2(s),t3(t);  

// cout< "<>;
}
return ans;
}

100pts:

考虑将第二维去掉,f[i]表示当前集合为i时的方案数,那么$f[i]=\sum f[i][j]$,如果不记录最后一层的话,每一个j都会被计算进去多次,所以容斥一下就可以了(稍不明白)。

#include
#include
#include
#include
#include
#define re register
#define co const
#define rec re co
#define LL long long
using namespace std;
const int mod=1e9+;
struct edge
{
int u,v,nxt;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].nxt
}ed[];
int first[],num_e;
#define f(x) first[x]
int n,m,ru[];
LL f[<<],g[<<]; int tem[<<][]; vector fr[];
bool pd(int s,int t)
{
bool ok=;
for(int i=;i<=n;i++)
if((<<i-)&t)
{
bool pd2=;
for(int j=;j<fr[i].size();j++)
if((<<fr[i][j]-)&s)pd2=;
ok&=pd2;
}
return ok;
}
LL find(rec int s,rec int po)
{
int res=;
for(int i=;i<fr[po].size();i++)
if((<<fr[po][i]-)&s)res++;
return res;
}
int pw[];
int siz[<<];
LL poww(LL a,int b);
inline void add(rec int u,rec int v);
signed main()
{
// freopen("obelisk9.in","r",stdin);

 cin>>n>>m;int tu,tv;  
 for(re int i=;i<=m;i++)scanf("%d%d",&tu,&tv),add(tu,tv),ru\[tv\]++,fr\[tv\].push\_back(tu);

 for(re int i=;i<(<<n);i++){bitset<>t(i);siz\[i\]=t.count();}  
 for(re int i=;i<(<<n);i++)for(re int j=;j<=n;j++)tem\[i\]\[j\]=find(i,j);  
 f\[\]=;  
 pw\[\]=;for(int i=;i<=;i++)pw\[i\]=pw\[i-\]\*%mod;  
 for(int i=;i<=n;i++)g\[<<i-\]=i;  
 int tttttt=(<<n);  
 for(re int i=;i<tttttt;i++)  
 {  
     int all=(~i)&(tttttt-);  
     for(re int k=all;k;k=(k-)&all)  
     {  
         int cnt=;  
         for(re int j=k;j;j-=(j&-j))  
             cnt+=tem\[i\]\[g\[j&-j\]\];

         if(siz\[k\]&)f\[i|k\]=(f\[i|k\]+f\[i\]\*pw\[cnt\]%mod);  
         else         f\[i|k\]=(f\[i|k\]-f\[i\]\*pw\[cnt\]%mod);  
         if(f\[i|k\]<)f\[i|k\]=f\[i|k\]%mod+mod;  
         if(f\[i|k\]>=mod)f\[i|k\]-=mod;  
     }  
 }

 printf("%lld\\n",(f\[(<<n)-\]%mod+mod)%mod);  

}
inline void add(rec int u,rec int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
LL poww(LL a,int b)
{
LL ans=;
while(b)
{
if(b&)ans=ans*a%mod;
a=a*a%mod;b=b>>;
}
return ans;
}