タイトル説明
OberthurですLeask巨大な魔法が有向グラフの非常に好きで、1日、彼は$ M $のエッジの有向グラフの$ n $のポイントを発見しました。
オベロイLeaskは有向グラフリングが美しいです信じていなかった、私はこの絵は美しいです持っているどのように多くの部分グラフ(すなわちエッジの選択セット)頼みますか?$ 1000000007 $剰余への答え。
入力形式
二つの整数のn $ $と$ M $の最初の行。
次の二つの行の各行$ M $有向エッジの整数を表します。ノー再ループフリーエッジいることを確認します。
出力フォーマット
$ 1000000007 $を法の整数の答えの行、。
サンプル
サンプル入力:
3 6
1 2
2 1
1 3
3 1
2 3
3 2
出力例:
25
データ範囲とヒント
$ 40 \%$データ$ n個の\ leqslant 5、m個の\のため leqslant $ 20、
$ 60 \%$データ$ n個の\ leqslant 10 $のために、
$ \ 80%$データ$ n個の\ leqslant 15 $のために、
$ \ 100用%$データの$ n \は17 $をleqslant。
問題の解決策
データ範囲が$ 17 $で参照してください、私はこの$ AK $をテストすることができると思います。私が見たときにしかし、それはエッジを設定する時間ですが、私は部屋で半時間後にはほとんどのキーボードの音を聞いてことがわかった......
同様に、我々は最初の練習の$ 60 \%$を考えます。
第一層の度合いが$ 0 $点で階層非循環有向グラフを与え、第2の層は、$ topsort $に類似度$ 0 $点の削除の第一の層は?
次に[I] [J] $ノードの現在選択されたセットを示す$のDPを設定は、$ iが、ノード$ J $のプログラム番号のセットの最終層を$です。
S_1が$ $ノードの現在選択されたセットを示していると仮定することができる、S_2 $ $は、ノードの集合の最後の層、S_3 $ $ $補体S_1、S_2および$ $ $の$および$ S_3連結側の部分集合、$を表しS_1 \ oplus S_2 $と$ S_3 $接続側は$ cnt_1 $バー、$ S_2 $ $ $ $ S_3 cnt_2 $ストリップの側に接続されています。
その後、状態遷移方程式は、次のとおり
$$ DP [S_1 | S_3] [S_3] = \和[S_1] [S_2] DP回\ \回\ PROD 2 ^ {cnt_1}(2 ^ {cnt_2} -1)$$
このような時間の複雑さは、$ \シータである$(4 ^ n回mを\)、のいずれかのスペースや時間が十分ではありません。
したがって、我々は、現在選択されているノードの直接の列挙は、その補数のサブセットを列挙し、その後$ S_2 $、$ S_1 $を設定し、さらに$ S_1 $ $ $ S_2 $側面が持っている間で設定二次元を引く考えますCNT $ストリップ。
[| S_2 S_1] = DP [S_1] \回2 ^ {CNT} $ $のDP:さて、あなたは、遷移方程式のような状態で表示されることがあります。
S_2 $は$ S_1 $と$ S_2構成$多くのことなので、我々はまた、係数$との包含と除外を必要とする( - 1)^ {SZ [S] 1 |しかし、あなたは答えは限り$ S_1をカウントすることがわかります} $。
$ \シータ(3 ^ nm)の$のために、この時間の時間複雑さは、たったの$、80 $の点は、また、最適化がたくさんある、最適化する必要があります、私は$ \シータ(2 ^ n個の\ n倍^ 2にそれを最適化することができます)$、あなたはAC $を$できるように、この質問です。
時間複雑:$ \シータ(2 ^ n個の\ n倍^ 2)$。
期待はスコア:$ $ 100ポイントを。
実際のスコア:$ $ 100ポイント。
コードの時間
#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n,m;
bool Map[20][20];
int sz[131073];
int du[65537];
int sum[131073];
int qpow[1000];
int dp[131073];
int main()
{
scanf("%d%d",&n,&m);
int maxn=(1<<n);
dp[0]=qpow[0]=1;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
Map[x-1][y-1]=1;
qpow[i]=(qpow[i-1]<<1)%mod;
}
sz[0]=-1;
for(int i=1;i<maxn;i++)sz[i]=sz[i>>1]*(i&1?-1:1);
for(int i=0;i<maxn-1;i++)
{
if(!dp[i])continue;
int t=maxn-1-i;
memset(du,0,sizeof(du));
for(int j=0;j<n;j++)
if(i&(1<<j))
for(int k=0;k<n;k++)
du[1<<k]+=Map[j][k];
sum[0]=0;
for(int s=(t-1)&t;;s=(s-1)&t)
{
int now=t^s,lst=now&-now;
sum[now]=sum[now-lst]+du[lst];
dp[i+now]=(dp[i+now]+1LL*sz[now]*qpow[sum[now]]*dp[i])%mod;
if(!s)break;
}
}
printf("%lld",dp[maxn-1]);
return 0;
}
rp++