Submission #1842896
Source Code Expand
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int (i)=0;(i)<(int)(n);(i)++)
#define rept(i,n) for(int (i)=0;(i)<=(int)(n);(i)++)
#define reps(i,s,n) for(int (i)=(s);(i)<(int)(n);(i)++)
#define repst(i,s,n) for(int (i)=(s);(i)<=(int)(n);(i)++)
#define repr(i,n) for(int (i)=(n);(i)>=0;(i)--)
#define each(itr,v) for(auto (itr):(v))
#define all(c) (c).begin(),(c).end()
#define pb push_back
#define mp(x,y) make_pair((x),(y))
#define fi first
#define se second
#define chmin(x,y) x=min(x,y)
#define chmax(x,y) x=max(x,y)
#define ln "\n"
#define show(x) cout << #x << " = " << x ln
#define dbg(x) cout<<#x"="<<x ln
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<vector<int> > mat;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = (int)1e9;
const ll linf = (ll)1e18;
const int mod = (int)(1e9+7);
const int dx[] = {0, 1, 0, -1};
const int dy[] = {1, 0, -1, 0};
const int ddx[] = {0, 1, 1, 1, 0, -1, -1, -1};
const int ddy[] = {1, 1, 0, -1, -1, -1, 0, 1};
struct oreno_initializer {
oreno_initializer() {
cin.tie(0);
ios::sync_with_stdio(0);
}
} oreno_initializer;
// ━━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…
// .。.:( ^ω^)・゚+.。.:( ^ω^)・゚+.。.:( ^ω^)・゚+.。.:( ^ω^)・゚+.。.:( ^ω^)・゚+
// ・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・‥…━━━☆・
int n, a, b;
vector<vi> e(100000);
ll dpf[100000], dpg[100000], f(int x, int p), g(int x, int p);
// xを親とする部分木に含まれる頂点を、両端が黒にならないように白か黒で塗る組み合わせの数
// pはxの親を表す 親に戻ってループしないように
ll f(int x, int p) {
if (dpf[x]>0) return dpf[x];
ll white = g(x,p);
ll black = 1;
rep(i,e[x].size()) {
int to = e[x][i];
if (to==p) continue;
black = (black * g(to, x))%mod;
}
return dpf[x] = (white + black)%mod;
}
// 頂点pを黒に塗っていてxが白でないといけない場合のxを親とする部分木の塗り方
ll g(int x, int p) {
if (dpg[x]>0) return dpg[x];
ll cnt = 1;
rep(i,e[x].size()) {
int to = e[x][i];
if (to==p) continue;
cnt = (cnt * f(to, x))%mod;
}
return dpg[x] = cnt;
}
int main() {
cin >> n;
rep(i,n-1) {
cin >> a >> b;
a--, b--;
e[a].pb(b), e[b].pb(a);
}
/*
* treeDPってやつらしい ↓のスライドがすごくわかりやすかった
* http://keita-matsushita.hatenablog.com/entry/2016/12/30/180918
* まずf(0,-1)が木全体の塗り方の総数(求めたい答え)を表す
* 例えば0から頂点1,2,3に辺が伸びているとすると、f(0,-1)は各部分木(0-1-..、0-2-..、0-3-..と続く3つ)の
* (0を黒に塗った場合のこの部分木の他の頂点の塗り方の総数) + (0を白に塗った場合の...)
* の和で求められる 前者の場合0と隣接している頂点は絶対白で塗る必要があるのでg(to,0)で表せる
* 後者だと0と隣接している頂点は白で塗っても黒で塗ってもいいのでf(to,0)
* ってことでf(0,-1) = f(1,0)+f(2,0)+f(3,0) + g(1,0)+g(2,0)+g(3,0)となる
* これを再帰で繰り返して木全体を探索すればいい
*/
ll ans = f(0, -1);
cout << ans << ln;
}
Submission Info
Submission Time |
|
Task |
D - 塗り絵 |
User |
creep04 |
Language |
C++14 (GCC 5.4.1) |
Score |
100 |
Code Size |
3609 Byte |
Status |
AC |
Exec Time |
43 ms |
Memory |
7424 KB |
Judge Result
Set Name |
All |
Score / Max Score |
100 / 100 |
Status |
|
Set Name |
Test Cases |
All |
000.txt, 001.txt, 002.txt, 003.txt, 004.txt, 005.txt, 006.txt, 007.txt, 008.txt, 009.txt, 010.txt, 011.txt, 012.txt, 013.txt, 014.txt, 015.txt, 016.txt, 017.txt, 018.txt, 019.txt, 020.txt, 021.txt |
Case Name |
Status |
Exec Time |
Memory |
000.txt |
AC |
3 ms |
2560 KB |
001.txt |
AC |
3 ms |
2560 KB |
002.txt |
AC |
33 ms |
6400 KB |
003.txt |
AC |
43 ms |
7424 KB |
004.txt |
AC |
28 ms |
5760 KB |
005.txt |
AC |
39 ms |
7424 KB |
006.txt |
AC |
39 ms |
7040 KB |
007.txt |
AC |
43 ms |
7424 KB |
008.txt |
AC |
30 ms |
6016 KB |
009.txt |
AC |
43 ms |
7424 KB |
010.txt |
AC |
31 ms |
6016 KB |
011.txt |
AC |
43 ms |
7424 KB |
012.txt |
AC |
25 ms |
5504 KB |
013.txt |
AC |
39 ms |
7424 KB |
014.txt |
AC |
20 ms |
4864 KB |
015.txt |
AC |
43 ms |
7424 KB |
016.txt |
AC |
26 ms |
5504 KB |
017.txt |
AC |
43 ms |
7424 KB |
018.txt |
AC |
9 ms |
3456 KB |
019.txt |
AC |
43 ms |
7424 KB |
020.txt |
AC |
6 ms |
3072 KB |
021.txt |
AC |
43 ms |
7424 KB |