Wannafly 挑戰賽27 題解
阿新 • • 發佈:2018-12-18
Wannafly 挑戰賽27
題目連線
A.灰魔法師
題目
題解
考慮到可能的完全平方數只有多個,因此對於每種數,直接暴力列舉所有的完全平方數計算一下就可以了.
程式碼
#include <iostream>
#define int long long
const int N = 100007;
int a[N];
int n;
int p2[N];
int tot;
signed main() {
for(int i = 1;;i++) {
int a = i*i;
if(a > 2*N) break;
p2[tot++ ] = a;
}
std::cin >> n;
for(int i = 1;i <= n;++i) {
int tmp;
std::cin >> tmp;
a[tmp] ++;
}
int ans = 0;
for(int i = 1;i <= 100000;++i) {
if(a[i] == 0) continue;
for(int j = 0;j < tot;++j) {
int an = p2[j] - i;
if (an < i) continue;
if(an == i) ans += a[i]*(a[i]-1)/2;
else if(an <= 100000)ans += a[i]*a[an];
}
}
std::cout << ans << std::endl;
}
B.紫魔法師
題目
題解
注意到至少當存在一個奇環的情況下,一定需要種顏色,而其他情況下只要種顏色就足夠了.
只需要用tarjan演算法求其點雙連通分量的大小即可.
程式碼
#include <iostream>
#include <algorithm>
#include <cstring>
#include <stack>
#include <vector>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
const int N = 100007;
struct edge{
int u,v,nxt;
}es[N<<2];
int head[N],cnt;
int vis[N],dfn[N],low[N],idx;
int v[N<<1],u[N<<1];
std::stack<int> stk;
void addedge(int u,int v) {
es[cnt].u = u;es[cnt].v = v;es[cnt].nxt = head[u];head[u] = cnt++;
}
int flag;
void tarjan(int u,int fa) {
dfn[u] = low[u] = ++idx;
vis[u] = 1;
for(int e = head[u];e != -1;e = es[e].nxt) {
int v = es[e].v;
if(v == fa) continue;
stk.push(e);
if(!vis[v]) {
tarjan(v,u);
if(low[u] > low[v]) low[u] = low[v];
if(dfn[u] <= low[v]) {
//割點
int cnt = 0;
while(true) {
int se = stk.top();stk.pop();
cnt++;
if(se == e) break;
}
if(cnt > 1 && cnt % 2 != 0) {
flag = 1;
}
}
}
else low[u] = std::min(low[u],dfn[v]);
}
}
int n,m;
int main() {
memset(head,-1,sizeof(head));
std::ios::sync_with_stdio(false);
std::cin >> n >> m;
rep(i,1,m) {
int u,v;
std::cin >> u >> v;
addedge(u,v);
addedge(v,u);
}
tarjan(1,0);
if(flag) std::cout << "3" << std::endl;
else std::cout << "2" << std::endl;
return 0;
}
C.藍膜法師
題目
題解
樹形dp
狀態定義
定義表示子樹中包含節點的連通塊大小為,且其餘聯通塊大小均的方案數.
注意上述定義中是沒有意義的,我們再定義.
轉移方程的計算:
當我們要計運算元樹的值的時候,其兒子節點分別為.
如果我們將通向兒子節點的某條邊切斷,那麼這個兒子對節點聯通塊大小的貢獻就沒了,但是它的方案數可以取,這也就是我們定義的意義所在了,很巧妙地,就剛好等於兒子對的聯通塊貢獻為時的方案數.
那麼方程就得到了
最後答案就是
實現方式
我們可以先將與合併,再將與合併…
程式碼
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
const int N = 2018;
typedef long long LL;
const LL P = 998244353;
std::vector<int> edge[N];
LL dp[N][N];
LL tmp[N];
int sz[N];
int n,k;
void dfs(int u,int fa) {
sz[u] = 1;
dp[u][1] = 1;
for(int v : edge[u]) {
if(v == fa) continue;
dfs(v,u);
memset(tmp,0,sizeof(tmp));
for(int i = 1;i <= sz[u];++i) {
for(int j = 0;j <= sz[v] && i + j <= k;++j) {
tmp[i+j] = (tmp[i+j] + (dp[u][i] * dp[v][j] % P)) % P;
}
}
for(int i = 1;i <= k;++i)
dp[u][i] = tmp[i];
sz[u] += sz[v];
}
for(int i = 1;i <= k;++i)
dp[u][0] = (dp[u][0] + dp[u][i]) % P;
}
int main () {
std::ios::sync_with_stdio(false);
std::cin >> n >> k;
for(int i = 0;i < n-1;++i) {
int u,v;
std::cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs(1,0);
std::cout << dp[1][0] << std::endl;
return 0;
}