Submission #1214364

#TimeUsernameProblemLanguageResultExecution timeMemory
1214364nrg_studioStar Trek (CEOI20_startrek)C++20
100 / 100
80 ms15944 KiB
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define pb push_back
#define all(x) x.begin(),x.end()
#define vec vector

const int MAX_N = 1e5;
vec<int> adj[MAX_N];
int state_down[MAX_N], state_up[MAX_N], state[MAX_N];
ll L = 0; // amount of losing nodes
ll S_L = 0, S_W = 0;
int rt = 0;

void dfs_state_down(int cur) { // for each node: counts amount of children that are losing
    for (int x : adj[cur]) {
        adj[x].erase(find(all(adj[x]),cur));
        dfs_state_down(x);
        state_down[cur] += !state_down[x];
    }
}
void dfs_state_up(int cur) { // for each node: counts if the complement of parent's subtree is losing
    state[cur] = (state_up[cur]+state_down[cur]); // this node is winning iff at least one adjacent node is losing
    L += !state[cur];
    for (int x : adj[cur]) {
        state_up[x] = !(state_up[cur]+(state_down[cur]-!state_down[x]));
        dfs_state_up(x);
    }
}

int lose_down[MAX_N], lose_up[MAX_N], lose[MAX_N];

void dfs_lose_down(int cur) { // for each node, when restricted to its subtree: calculate amount of losing nodes, then when change states, propagates a change in this node's state
    lose_down[cur] = !state_down[cur];
    for (int x : adj[cur]) {
        dfs_lose_down(x);
        if (state_down[cur]==1 && !state_down[x]) { // casework
            lose_down[cur] += lose_down[x];
        } else if (!state_down[cur]) {
            lose_down[cur] += lose_down[x];
        }
    }
}
void dfs_lose_up(int cur) { // same thing as lose_down, but restrict it to the complement of the subtree
    lose_up[cur] += !state_up[cur]; // base cases

    lose[cur] = !state[cur];
    if (state[cur]==1) { // combining answers for inside and outside subtree
        for (int x : adj[cur]) { // if node is winning state, then can change it to losing if exactly one adjacent neighbour is losing
            if (!state_down[x]) {
                lose[cur] += lose_down[x];
            } // find that neighbour, and update
        }
        if (state_up[cur]) {lose[cur] += lose_up[cur];}
    } else if (!state[cur]) { // if node is losing, then all adjacent are winning: can change any neighbour's state
        for (int x : adj[cur]) {lose[cur] += lose_down[x];}
        lose[cur] += lose_up[cur]-1; // so sum up answers for all neighbours
    }
    if (state[cur]) {S_W += lose[cur];} // sum of lose for winning states
    else {S_L += lose[cur];} // sum of lose for losing states

    int sum1 = 0, sum2 = 0;
    for (int x : adj[cur]) { // to speed up dp transition to children
        if (state_down[x]) {sum1 += lose_down[x];}
        else {sum2 += lose_down[x];}
    }

    for (int x : adj[cur]) {
        if (state_up[x]) { // casework (follows similar logic as calculating lose[cur])
            lose_up[x] = sum1+lose_up[cur]-(state_down[x] ? lose_down[x] : 0);
        } else {
            if (state_up[cur]+state_down[cur]-!state_down[x]==1) {
                lose_up[x] = (state_up[cur] ? lose_up[cur] : sum2-(!state_down[x] ? lose_down[x] : 0));
            }
        }
        dfs_lose_up(x);
    }
}

const ll mod = 1e9+7;
ll add(ll a, ll b) {ll c = (a+b)%mod; if (c<0) {c+=mod;} return c;}
ll mul(ll a, ll b) {return a*b%mod;}
ll exp(ll a, ll b) {
    ll c = 1;
    while (b) {
        if (b&1) {c = mul(c,a);}
        a = mul(a,a); b /= 2;
    } return c;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);

    int n; ll d; cin >> n >> d;
    for (int i=0;i<n-1;i++) {
        int a, b; cin >> a >> b;
        adj[--a].pb(--b);
        adj[b].pb(a);
    }
    for (int i=0;i<n;i++) {if (adj[i].size()>1) {rt = i;}}

    memset(state_down,0,sizeof(state_down));
    state_up[rt] = 0;
    dfs_state_down(rt); // rerooting: is each node losing or winning
    dfs_state_up(rt);

    dfs_lose_down(rt);
    dfs_lose_up(rt);

    d--; // now use geometric series to calculate the answer for the first parallel universe
    ll S = add(S_W,-S_L), R = mul(mul(n,n),exp(S,mod-2));
    ll geosum = mul(mul(L,mul(n,n)),exp(S,d-1)) * add(1,-exp(R,d)) %mod * exp(add(1,-R),mod-2) %mod;

    L = mul(exp(S,d),L);
    L = add(L,geosum);
    ll tot = exp(mul(n,n),d+1);
    
    ll ans;
    if (state[0]) {ans = add(tot,-mul(lose[0],L));} // now calculate the answer for node 1 in the original universe
    else {ans = mul(lose[0],L);}

    cout << ans << '\n';
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...