zoukankan      html  css  js  c++  java
  • 必做题

    主要是一些讨论复杂或者容易写错的模拟

    1. CF912C

    题意:$n$个敌人,给定每个敌人初始血量,最大血量,每秒恢复血量,还有$m$个更新操作,需要选出一个时间$t$释放技能,使得得分最大

    2. CF118C

    题意:给定串s,每次修改花费为新数与原数差的绝对值,求使$s$中至少$k$个相同的数时的最小代价,输出字典序最小的方案

    3. CF704C

    题意:给定$n$个表达式,$m$个变量,每个变量出现次数$le 2$,每个表达式至多含$2$个变量,求$2^m$种情况中有多少种情况使得$n$个表达式异或值为$1$ 

    4. 2019icpcnanjingonlineG

    题意:给定四条边范围,求多少种情况能够成四边形

    5. CF1320D

    题意:给定01串,每次询问两个子串,是否能从其中一个变换为另一个,每次变换把"110"变为"011"或"011"变为"110"

    6. gym102576I

    题意:给定大数,求拆分为不超过25个回文数的和

    7. CF1428D

    题意:$n imes n$的网格,每行每列至多$2$个障碍,给定从每个位置往上扔回旋镖撞到障碍数,求构造合法方案

    8. CF1332F

    题意:给定树,求所有边集非空的边导出子图的独立集个数

    gym102500A

    题意:给定$w$天每人得分增加情况,求每个人平均排名

    CF623C

    题意:给定平面$n$个点,求把每个点$(x_i,y_i)$变为$(x_i,0)$或$(0,y_i)$,使得任意两点距离最大值最小

    2020icpcxiaomionline2J

    题意:定义了序列$S$,给定长$n$序列$a$,求$S$每个长$n$子串与$a$的汉明距离和以及最小汉明距离

    CF1252B

    gym102431H

    gym102700L 

    gym102439E

    CF1422D

    gym101620D 

    hdu6580

    gym102501H

    1. 考虑最优释放时间,一定是某个敌人血量增加到超过damage的前一秒或者一次更新操作的前一秒,用一个优先队列维护所有更新事件

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    struct event {
        int64_t time,val;
        int id,type;
        bool operator < (const event &rhs) const {
            if (time!=rhs.time) return time>rhs.time;
            return type>rhs.type;
        }
    };
    priority_queue<event> q;
    int max_health[N],regen[N];
    bool can[N];
    int64_t vis[N];
    int main() {
        int n, m, bounty, increase, damage;
        scanf("%d%d%d%d%d", &n, &m, &bounty, &increase, &damage);
        int cnt = 0;
        for (int i=0; i<n; ++i) {
            int start_health;
            scanf("%d%d%d", &max_health[i], &start_health, &regen[i]);
            if (max_health[i]<=damage) { 
                if (increase) return puts("-1"),0;
                ++cnt;
                continue;
            }
            if (start_health<=damage) { 
                ++cnt;
                can[i] = 1;
                if (regen[i]) { 
                    q.push({(damage-start_health)/regen[i],0ll,i,2});
                    q.push({(damage-start_health)/regen[i]+1,0ll,i,1});
                }
            }
        }
        for (int i=0; i<m; ++i) {
            int time,enemy,health;
            scanf("%d%d%d", &time, &enemy, &health);
            if (max_health[enemy-1]<=damage) continue;
            q.push({time,health,enemy-1,0});
            q.push({time-1,0ll,enemy-1,2});
        }
        int64_t ans = 0;
        while (q.size()) {
            auto u = q.top(); q.pop();
            if (u.type==2) ans = max(ans, (bounty+(int64_t)u.time*increase)*cnt);
            else if (u.type==1) {
                if (u.val!=vis[u.id]) continue;
                can[u.id] = 0;
                --cnt;
            }
            else {
                vis[u.id] = u.time;
                if (u.val<=damage) {
                    if (!can[u.id]) can[u.id] = 1, ++cnt;
                    if (regen[u.id]) {
                        q.push({u.time+(damage-u.val)/regen[u.id],u.time,u.id,2});
                        q.push({u.time+(damage-u.val)/regen[u.id]+1,u.time,u.id,1});
                    }
                }
                else if (can[u.id]) can[u.id] = 0, --cnt;
            }
        }
        if (cnt) { 
            if (increase) ans = -1;
            else ans = max(ans, bounty*(int64_t)cnt);
        }
        printf("%lld
    ", ans);
    }
    View Code

    2. 先求出最小花费,然后贪心枚举前缀,判断后缀是否合法即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e4+10, INF = 1e9;
    int cnt[10],cur[10];
    char s[N];
    int main() {
        int n, k;
        scanf("%d%d%s", &n, &k, s+1);
        for (int i=1; i<=n; ++i) ++cnt[s[i]-='0'];
        //当前每种数为cur,可修改的数为cnt时的最小花费
        auto gao = [&]() {
            int ans = INF;
            for (int num=0; num<10; ++num) {
                int now = cur[num], cost = 0;
                for (int d=0; d<10; ++d) {
                    int x = num+d, t;
                    if (x<10) {
                        t = min(k-now, cnt[x]);
                        now += t;
                        cost += t*d;
                    }
                    int y = num-d;
                    if (y!=x&&y>=0) {
                        t = min(k-now, cnt[y]);
                        now += t;
                        cost += t*d;
                    }
                }
                if (now==k) ans = min(ans, cost);
            }
            return ans;
        };
        int mi = gao();
        printf("%d
    ", mi);
        for (int i=1; i<=n; ++i) {
            --cnt[s[i]];
            for (int u=0; u<10; ++u) {
                ++cur[u];
                int cost = gao()+abs(s[i]-u);
                if (cost==mi) {
                    putchar(u+'0');
                    mi -= abs(s[i]-u);
                    break;
                }
                --cur[u];
            }
        }
        puts("");
    }
    View Code

    3. 一个括号看做一个点,一个变量相当于连接两个点,可以看做边,由于每个点度数不超过$2$,这样得到的图一定是若干环和链,分别对每个连通块dp一下,最后再dp合并。实现略复杂,需要特判孤立点和自环 

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10, P = 1e9+7;
    int n, m, sz[N], L[N], R[N], vis[N], deg[N];
    vector<pair<int,int>> g[N];
    void add(int &a, int64_t b) {a=(a+b)%P;}
    array<int,2> solve1(int x) {
        int dp[2][2][2];
        memset(dp,0,sizeof dp);
        if (sz[x]==2) {
            if (g[x][0].second) { 
                dp[0][0][1] = dp[0][1][1] = 1;
                dp[0][1][0] = 2;
            }
            else { 
                dp[0][0][0] = dp[0][1][0] = 1;
                dp[0][1][1] = 2;
            }
        }
        else {
            if (g[x][0].second) dp[0][0][1] = dp[0][1][0] = 1;
            else dp[0][0][0] = dp[0][1][1] = 1;
        }
        vis[x] = 1;
        x = g[x][0].first;
        int cur = 0;
        array<int,2> c{};
        while (1) {
            cur ^= 1;
            memset(dp[cur],0,sizeof dp[0]);
            vis[x] = 1;
            if (g[x].size()==1&&vis[g[x][0].first]) {
                if (sz[x]==2) { 
                    for (int u=0; u<2; ++u) for (int v=0; v<2; ++v) {
                        int w = dp[cur^1][u][v];
                        add(c[u^v],w), add(c[u^1],w);
                    }
                }
                else { 
                    for (int u=0; u<2; ++u) for (int v=0; v<2; ++v) add(c[u^v],dp[cur^1][u][v]);
                }
                break;
            }
            else {
                int tp = vis[g[x][0].first];
                for (int u=0; u<2; ++u) for (int v=0; v<2; ++v) {
                    int w = dp[cur^1][u][v];
                    if (g[x][tp].second) { 
                        add(dp[cur][u^v][1],w);
                        add(dp[cur][u^1][0],w);
                    }
                    else {
                        add(dp[cur][u^v][0],w);
                        add(dp[cur][u^1][1],w);
                    }
                }
                x = g[x][tp].first;
            }
        }
        return c;
    }
    array<int,2> solve2(int x) {
        int dp[2][2][2][2];
        memset(dp,0,sizeof dp);
        if (g[x][0].second==0&&g[x][1].second==0) {
            dp[0][0][0][0] = dp[0][1][1][0] = dp[0][1][0][1] = dp[0][1][1][1] = 1;
        }
        else if (g[x][0].second&&g[x][1].second==0) {
            dp[0][0][1][0] = dp[0][1][0][0] = dp[0][1][1][1] = dp[0][1][0][1] = 1;
        }
        else if (g[x][0].second==0&&g[x][1].second) {
            dp[0][0][0][1] = dp[0][1][1][1] = dp[0][1][0][0] = dp[0][1][1][0] = 1;
        }
        else {
            dp[0][0][1][1] = dp[0][1][0][1] = dp[0][1][1][0] = dp[0][1][0][0] = 1;
        }
        vis[x] = 1;
        x = g[x][0].first;
        int cur = 0;
        array<int,2> c{};
        while (1) {
            cur ^= 1;
            memset(dp[cur],0,sizeof dp[0]);
            vis[x] = 1;
            int tp;
            if (!vis[g[x][0].first]) tp = 0;
            else if (!vis[g[x][1].first]) tp = 1;
            else { 
                for (int u=0; u<2; ++u) for (int v=0; v<2; ++v) for (int z=0; z<2; ++z) add(c[u^(v|z)],dp[cur^1][u][v][z]);
                break;
            }
            for (int z=0; z<2; ++z) {
                for (int u=0; u<2; ++u) for (int v=0; v<2; ++v) {
                    int w = dp[cur^1][u][v][z];
                    if (g[x][tp].second) {
                        add(dp[cur][u^v][1][z],w);
                        add(dp[cur][u^1][0][z],w);
                    }
                    else {
                        add(dp[cur][u^v][0][z],w);
                        add(dp[cur][u^1][1][z],w);
                    }
                }
            }
            x = g[x][tp].first;
        }
        return c;
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i=1; i<=n; ++i) {
            int t, x;
            scanf("%d", &t);
            sz[i] = t;
            while (t--) {
                scanf("%d", &x);
                if (L[abs(x)]==0) {
                    if (x<0) L[abs(x)] = -i;
                    else L[x] = i;
                }
                else {
                    if (x<0) R[abs(x)] = -i;
                    else R[x] = i;
                }
            }
        }
        int cnt = 0;
        for (int x=1; x<=m; ++x) {
            if (!L[x]) ++cnt;
            else if (R[x]) { 
                if (L[x]==R[x]) {
                    deg[abs(L[x])] = -1;
                    continue;
                }
                else if (L[x]==-R[x]) {
                    deg[abs(L[x])] = -2;
                    continue;
                }
                if (L[x]<0&&R[x]<0) L[x]=-L[x],R[x]=-R[x];
                if (L[x]>0&&R[x]>0) { 
                    g[L[x]].push_back({R[x],0});
                    g[R[x]].push_back({L[x],0});
                    ++deg[L[x]];
                    ++deg[R[x]];
                }
                else {
                    L[x] = abs(L[x]), R[x] = abs(R[x]);
                    g[L[x]].push_back({R[x],1});
                    g[R[x]].push_back({L[x],1});
                    ++deg[L[x]];
                    ++deg[R[x]];
                }
            }
        }
        array<int64_t,2> c{};
        c[0] = 1;
        for (int i=1; i<=n; ++i) { 
            if (deg[i]==0&&sz[i]==2) { 
                c = {(c[0]+c[1]*3)%P,(c[0]*3+c[1])%P};
                vis[i] = 1;
            }
            else if (deg[i]==0||deg[i]==-1) { 
                c = {(c[0]+c[1])%P,(c[0]+c[1])%P};
                vis[i] = 1;
            }
            else if (deg[i]==-2) {
                c = {2*c[1]%P,2*c[0]%P};
                vis[i] = 1;
            }
        }
        for (int i=1; i<=n; ++i) if (deg[i]==1&&!vis[i]) { 
            auto t = solve1(i);
            c = {(c[0]*t[0]+c[1]*t[1])%P,(c[0]*t[1]+c[1]*t[0])%P};
        }
        for (int i=1; i<=n; ++i) if (!vis[i]) {
            auto t = solve2(i);
            c = {(c[0]*t[0]+c[1]*t[1])%P,(c[0]*t[1]+c[1]*t[0])%P};
        }
        for (int i=1; i<=cnt; ++i) c[1] = c[1]*2%P;
        printf("%lld
    ", c[1]);
    }
    View Code

    4. 枚举每条边作为最大值的情况,那么剩余三条边方案数形式是$sum limits_{i}sum limits_{j}sum limits_{k} [i+j+k>x]$,暴力分类讨论即可$O(1)$算,总复杂度就是$O(T(a+b+c+d))$

    当然也可以再分类讨论去掉最外层min,从而优化到$O(T)$,但是过于麻烦

    #include <bits/stdc++.h>
    using namespace std;
    int64_t s0(int l, int r) {
        return r-l+1;
    }
    int64_t s1(int l, int r) {
        return (r+l)*(r-l+1ll)/2;
    }
    int64_t s2(int64_t l, int64_t r) {
        return (r-l+1)*(2*l*l+2*r*r+2*l*r+r-l)/6;
    }
    int64_t solve(int l1, int r1, int l2, int r2, int l3, int r3, int x) {
        if (l3>r3||l1>r1||l2>r2) return 0;
        int64_t ans = 0;
        int lx = max(l1,x-l2-l3+1), rx = r1;
        if (lx<=rx) ans += s0(l2,r2)*s0(l3,r3)*s0(lx,rx);
        lx = max(l1,x-l3+1-r2), rx = min(r1, x-l2-l3);
        if (lx<=rx) ans += s0(l3,r3)*(s0(lx,rx)*(r2-x+l3)+s1(lx,rx));
        lx = max(l1, x-l2+1-r3), rx = min(r1, x-l3-r2);
        if (lx<=rx) ans += (s0(l2,r2)*(r3-x)+s1(l2,r2))*s0(lx,rx)+s0(l2,r2)*s1(lx,rx);
        lx = max({l1,x-l2+1-r3,x-l3-r2+1}), rx = min(r1,x-l2-l3);
        int64_t ret = 0;
        if (lx<=rx) {
            ans += s0(lx,rx)*(1+x-l3-l2)*(r3-x)-s2(lx,rx)+s1(lx,rx)*(1+2*x-l3-l2-r3);
            ret += s0(lx,rx)*(l2+x-l3)*(x-l3-l2+1)-s1(lx,rx)*(x-l3+x-l3+1)+s2(lx,rx);
        }
        lx = max(l1,x-r2+1-r3), rx = min({r1,x-l2-r3,x-l3-r2});
        if (lx<=rx) {
            ans += s0(lx,rx)*(r2-x+r3)*(r3-x)+s1(lx,rx)*(r2-2*x+2*r3)+s2(lx,rx);
            ret += s0(lx,rx)*(r2-x+r3)*(r2+x+1-r3)+s1(lx,rx)*(x-r3+x-r3+1)-s2(lx,rx);
        }
        if (r3-l3>=1) {
            int lx = max(l1,x-l3-r2+1), rx = min(r1,x-l2-r3);
            if (lx<=rx) { 
                ans += s0(lx,rx)*(r3-l3)*(r3-x)+s1(lx,rx)*(r3-l3);
                ret += s0(lx,rx)*(r3-l3)*(2*x+1-l3-r3)+s1(lx,rx)*(2*l3-2*r3);
            }
        }
        return ans+ret/2;
    }
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            int L[4], R[4];
            for (int i=0; i<4; ++i) scanf("%d%d", &L[i], &R[i]);
            int64_t ans = 0;
            for (int x=L[0]; x<=R[0]; ++x) { 
                ans += solve(L[1],min(R[1],x),L[2],min(R[2],x),L[3],min(R[3],x),x);
            }
            for (int x=L[1]; x<=R[1]; ++x) {
                ans += solve(L[0],min(R[0],x-1),L[2],min(R[2],x),L[3],min(R[3],x),x);
            }
            for (int x=L[2]; x<=R[2]; ++x) {
                ans += solve(L[0],min(R[0],x-1),L[1],min(R[1],x-1),L[3],min(R[3],x),x);
            }
            for (int x=L[3]; x<=R[3]; ++x) {
                ans += solve(L[0],min(R[0],x-1),L[1],min(R[1],x-1),L[2],min(R[2],x-1),x);
            }
            printf("%lld
    ", ans);
        }
    }
    View Code

    5. 可以观察到两个串如果每个长为奇数的极长连续1段的左侧0的个数都相同,那么就可以互相转换,否则一定不能

    考虑hash,把每个0看做1,把长为奇数的极长连续1段看做1个2,长为偶数的1直接忽略即可
    询问的时候特判下端点情况,相当于要把几段hash值合并 

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10;
    const int P = 876756319, B = 99999991;
    int fac[N], f[N], sum[N], cnt[N], L[N], R[N];
    char s[N];
    int main() {
        int n;
        scanf("%d%s", &n, s+1);
        fac[0] = 1;
        for (int i=1; i<=n; ++i) fac[i] = fac[i-1]*(int64_t)B%P;
        int now = 0;
        for (int i=1; i<=n; ++i) {
            sum[i] = sum[i-1]+(s[i]=='0');
        }
        for (int i=1; i<=n; ++i) {
            if (s[i]=='1') {
                int j = i;
                while (j+1<=n&&s[j+1]=='1') ++j;
                for (int k=i; k<=j; ++k) {
                    L[k] = i;
                    R[k] = j;
                }
                if (j-i+1&1) f[j] = (f[i-1]*(int64_t)B+2)%P, cnt[j] = cnt[i-1]+1;
                else f[j] = f[i-1], cnt[j] = cnt[i-1];
                i = j;
            }
            else { 
                f[i] = (f[i-1]*(int64_t)B+1)%P;
                cnt[i] = cnt[i-1]+1;
            }
        }
        int q;
        scanf("%d", &q);
        while (q--) {
            int l1, l2, len;
            scanf("%d%d%d", &l1, &l2, &len);
            int r1 = l1+len-1, r2 = l2+len-1;
            if (sum[r1]-sum[l1-1]!=sum[r2]-sum[l2-1]) {
                puts("No");
                continue;
            }
            if (sum[r1]-sum[l1-1]==0||sum[r1]-sum[l1-1]==r1-l1+1) {
                puts("Yes");
                continue;
            }
            auto get = [&](int l, int r) {
                int len = cnt[r]-cnt[l]+1;
                int ans = (f[r]-f[l-1]*(int64_t)fac[len])%P;
                if (ans<0) ans += P;
                return pair<int,int>{ans,len};
            };
            auto add = [&](pair<int,int> a, pair<int,int> b) {
                int va = a.first, lena = a.second, vb = b.first, lenb = b.second;
                return pair<int,int>((va*(int64_t)fac[lenb]+vb)%P,lena+lenb);
            };
            auto Hash = [&](int l, int r) {
                pair<int,int> ans;
                if (R[l]) {
                    if (R[l]-l+1&1) ans = {2,1};
                    l = R[l]+1;
                }
                if (L[r]) {
                    ans = add(ans,get(l,L[r]-1));
                    if (r-L[r]+1&1) ans = add(ans,{2,1});
                }
                else ans = add(ans,get(l,r));
                return ans;
            };
            puts(Hash(l1,r1)==Hash(l2,r2)?"Yes":"No");
        }
    }
    View Code

    6. 每次将$n$贪心减掉最大的回文,一定不超过25

    具体实现就是从高位到低位枚举第一位不能与原数相等的位置,然后中间全填9

    做减法的时候没判$ile {len}_{now}$,WA了一发

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int len[N], cnt[N];
    char s[N], ans[25][N];
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            scanf("%s", s+1);
            int n = strlen(s+1);
            reverse(s+1,s+1+n);
            for (int i=1; i<=n; ++i) s[i] -= '0';
            int now = 0;
            auto gao = [&](char *t) {
                int ok = 1;
                for (int i=1; i<=n; ++i) if (s[i]!=s[n-i+1]) ok = 0;
                if (ok) {
                    for (int i=1; i<=n; ++i) t[i] = s[i];
                    return n;
                }
                for (int i=1; i<=n; ++i) cnt[i] = cnt[i-1]+(s[i]!=0);
                if (n>1&&cnt[n-1]==0&&s[n]==1) {
                    for (int i=1; i<=n-1; ++i) t[i] = 9;
                    return n-1;
                }
                int tp = 0;
                for (int i=n; i>=n-i+1; --i) {
                    if (i*2-1!=n) {
                        if (s[i]>s[n-i+1]) tp = 1;
                        else if (s[i]<s[n-i+1]) tp = 0;
                        if (cnt[i-1]-cnt[n-i+1]) continue;
                        if (tp) {
                            for (int j=n; j>i; --j) t[j] = t[n-j+1] = s[j];
                            t[i] = t[n-i+1] = s[i]-1;
                            for (int j=i-1; j>=n-i+2; --j) t[j] = 9;
                            return n;
                        }
                    }
                    else {
                        for (int j=n; j>i; --j) t[j] = t[n-j+1] = s[j];
                        if (tp) t[i] = s[i]-1;
                        else t[i] = s[i];
                        return n;
                    }
                }
                for (int i=n; i>n/2; --i) t[i] = t[n-i+1] = s[i];
                return n;
            };
            while (n) {
                len[now] = gao(ans[now]);
                for (int i=1; i<=n; ++i) {
                    if (i<=len[now]) s[i] -= ans[now][i];
                    if (s[i]<0) s[i] += 10, --s[i+1];
                }
                ++now;
                while (n&&!s[n]) --n;
            }
            printf("%d
    ", now);
            for (int i=0; i<now; ++i) { 
                for (int j=len[i]; j>=1; --j) putchar(ans[i][j]+'0');
                puts("");
            }
        }
    }
    View Code

    7. 经过分析可以发现如果$a_x=3$,那么要匹配一个$y(y>x)$,并且$a_y=1,2,3$

    如果$a_x=2$,那么要匹配一个$y(y>x)$,并且$a_y=1$

    然后就是考虑$2$和$3$分配优先级的问题,再画一下可以发现交换两个匹配位置的话产生影响是不变的

    所以只要碰到$1$就分给之前没匹配的$y$即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int a[N], h[N][2];
    int main() {
        int n;
        scanf("%d", &n);    
        for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
        for (int i=1; i<=n; ++i) h[i][0] = h[i][1] = 0;
        queue<int> q2, q3;
        auto chk = [&]() {
            int now = 1;
            for (int i=1; i<=n; ++i) {
                if (a[i]==1) {
                    if (q2.size()) { 
                        h[i][0] = h[q2.front()][0];
                        q2.pop();
                    }
                    else if (q3.size()) {
                        if (now>n) return false;
                        h[i][0] = now++;
                        h[i][1] = h[q3.front()][0];
                        q3.pop();
                    }
                    else { 
                        if (now>n) return false;
                        h[i][0] = now++;
                    }
                }
                else if (a[i]==2) {
                    if (q3.size()) {
                        if (now>n) return false;
                        h[i][0] = now++;
                        h[i][1] = h[q3.front()][0];
                        q3.pop();
                    }
                    else {
                        if (now>n) return false;
                        h[i][0] = now++;
                    }
                    q2.push(i);
                }
                else if (a[i]==3) {
                    if (q3.size()) {
                        if (now>n) return false;
                        h[i][0] = now++;
                        h[i][1] = h[q3.front()][0];
                        q3.pop();
                    }
                    else {
                        if (now>n) return false;
                        h[i][0] = now++;
                    }
                    q3.push(i);
                }
            }
            return q2.empty()&&q3.empty();
        };
        if (!chk()) puts("-1");
        else {
            int tot = 0;
            for (int i=1; i<=n; ++i) {
                if (h[i][1]) tot += 2;
                else if (h[i][0]) ++tot;
            }
            printf("%d
    ", tot);
            for (int i=1; i<=n; ++i) {
                if (h[i][0]) printf("%d %d
    ", h[i][0], i);
                if (h[i][1]) printf("%d %d
    ", h[i][1], i);
            }
        }
    }
    View Code

    8. 树dp题,设${dp}_{x,0}$表示以$x$为根的子树导出子图含$x$且$x$不在独立集内且$x$可以不与儿子相连的方案,${dp}_{x,1}$表示以$x$为根的子树导出子图含$x$且$x$在独立集内且$x$可以不与儿子相连的方案,${dp}_{x,0}$为以$x$为根的子树导出子图不含$x$的方案

    那么最终答案就是${dp}_{1,0}+{dp}_{1,1}-{dp}_{1,2}-1$

    假设$x$的儿子集合为$T$,那么枚举每个儿子的边连还是不连可以得到转移为

    ${dp}_{x,0}=sumlimits_{Ssubseteq T} prodlimits_{yin S} ({dp}_{y,0}+{dp}_{y,1}) prodlimits_{y ot in S}({dp}_{y,0}+{dp}_{y,1}-{dp}_{y,2})$

    ${dp}_{x,1}=sumlimits_{Ssubseteq T} prodlimits_{yin S}{dp}_{y,0} prodlimits_{y ot in S} ({dp}_{y,0}+{dp}_{y,1}-{dp}_{y,2})$

    ${dp}_{x,2}=prodlimits_{yin T} ({dp}_{y,0}+{dp}_{y,1}-{dp}_{y,2})$

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5+10, P = 998244353;
    vector<int> g[N];
    int dp[N][3];
    void dfs(int x, int f) {
        dp[x][0] = dp[x][1] = dp[x][2] = 1;
        for (int y:g[x]) if (y!=f) { 
            dfs(y,x);
            dp[x][2] = dp[x][2]*(dp[y][0]+dp[y][1]-(int64_t)dp[y][2])%P;
            dp[x][0] = dp[x][0]*(2ll*dp[y][0]+2ll*dp[y][1]-dp[y][2])%P;
            dp[x][1] = dp[x][1]*(2ll*dp[y][0]+dp[y][1]-dp[y][2])%P;
        }
    }
    
    int main() {
        int n;
        scanf("%d", &n);
        for (int i=1; i<n; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1,0);
        int ans = (dp[1][0]+dp[1][1]-(int64_t)dp[1][2]-1)%P;
        if (ans<0) ans += P;
        printf("%d
    ", ans);
    }
    View Code
  • 相关阅读:
    招隐-古琴曲-山中鸣琴,万籁声沉沉,何泠泠!
    因循苟且逸豫而无为,可以侥幸一时,而不可以旷日持久。——王安石
    模糊理论在图像处理中的应用
    铁关-中国首都警官合唱团-歌词
    听着总感觉莫名熟悉的音乐汇总
    石鼓歌-韩愈
    唐长安城
    唐长安的信仰——读书笔记
    Eclipse安装java web插件
    Java调用MySql数据库函数
  • 原文地址:https://www.cnblogs.com/fs-es/p/14123484.html
Copyright © 2011-2022 走看看