生活随笔
收集整理的這篇文章主要介紹了
[ZJOI2016]小星星
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
[ZJOI2016]小星星
題目描述
luogu題面
給定一個n個點的樹和n個點m條邊的無向圖,求將樹嵌入圖的方案數。
其中 n≤17,m≤n?(n?1)2n \leq 17,m \leq \frac{n*(n-1)}{2}n≤17,m≤2n?(n?1)?。
Solution
點數很少,考慮狀壓DP。
令f[i][j][k]f[i][j][k]f[i][j][k]表示以iii結點為根的子樹,iii號結點對應圖上的jjj號結點,kkk集合中的結點已經被使用了。
轉移就是枚舉iii的兒子sonsonson,枚舉兒子對應的結點為ttt,枚舉kkk的子集sss,f[i][j][k]=∑son,t,sf[son][t][s]?f[i][j][k?s]f[i][j][k]=\sum_{son,t,s} f[son][t][s]*f[i][j][k-s]f[i][j][k]=∑son,t,s?f[son][t][s]?f[i][j][k?s]
時間復雜度為O(3nnbalabala...)O(3^nn^{balabala...})O(3nnbalabala...),TLE。
考慮如何優化DP中枚舉集合的狀態。
有一種巧妙的容斥方法。
我們思考哪一些方案是不合法的,顯然倘若選出的若干樹上的結點對應了相同的圖上結點,這一種方案不合法。
我們不在DP中枚舉集合,而是讓他任意選擇,允許重復選同一個圖上節點。
枚舉可用的圖上結點集合s,用f[i][j]f[i][j]f[i][j]表示以iii結點為根的子樹,iii號結點對應圖上的jjj號結點,只能選擇圖上s集合包含的結點的方案數。
此時的最終答案Ans=∑s∑jf[1][j](?1)popcount(2n?1?s)Ans=\sum_{s} \sum_{j} f[1][j](-1)^{popcount(2^n-1-s)}Ans=∑s?∑j?f[1][j](?1)popcount(2n?1?s)
時間復雜度O(2nn3)O(2^nn^3)O(2nn3),luoguO2luogu\;\;O2luoguO2可過。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se secondusing namespace std
;template<typename T
>inline bool upmin(T
&x
,T y
) { return y
<x
?x
=y
,1:0; }
template<typename T
>inline bool upmax(T
&x
,T y
) { return x
<y
?x
=y
,1:0; }typedef long long ll
;
typedef unsigned long long ull
;
typedef long double lod
;
typedef pair
<int,int> PR
;
typedef vector
<int> VI
;const lod eps
=1e-11;
const lod pi
=acos(-1);
const int oo
=1<<30;
const ll loo
=1ll<<62;
const int mods
=998244353;
const int MAXN
=600005;
const int INF
=0x3f3f3f3f;
inline int read()
{int f
=1,x
=0; char c
=getchar();while (c
<'0'||c
>'9') { if (c
=='-') f
=-1; c
=getchar(); }while (c
>='0'&&c
<='9') { x
=(x
<<3)+(x
<<1)+(c
^48); c
=getchar(); }return x
*f
;
}
vector
<int> e
[20];
ll f
[20][20];
int pick
[20],flag
[20][20],n
,m
;
void tree_dp(int x
,int father
)
{for (auto v
:e
[x
]){if (v
==father
) continue;tree_dp(v
,x
);}for (int i
=1;i
<=n
;i
++) f
[x
][i
]=pick
[i
];for (auto v
:e
[x
]){if (v
==father
) continue;for (int j
=1;j
<=n
;j
++)if (pick
[j
]){ll sum
=0;for (int k
=1;k
<=n
;k
++)if (flag
[j
][k
]&&pick
[k
]) sum
+=f
[v
][k
];f
[x
][j
]*=sum
;}}
}
int main()
{n
=read(),m
=read();for (int i
=1;i
<=m
;i
++){int u
=read(),v
=read();flag
[u
][v
]=flag
[v
][u
]=1;}for (int i
=1;i
<n
;i
++){int u
=read(),v
=read();e
[u
].PB(v
),e
[v
].PB(u
);}ll ans
=0;for (int i
=1;i
<1<<n
;i
++){int opt
=((n
-__builtin_popcount(i
))&1)?-1:1;for (int j
=1;j
<=n
;j
++) pick
[j
]=(i
>>(j
-1))&1;tree_dp(1,0);for (int j
=1;j
<=n
;j
++) ans
+=f
[1][j
]*opt
;}printf("%lld\n",ans
);return 0;
}
總結
以上是生活随笔為你收集整理的[ZJOI2016]小星星的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。