HDU6403 Card Game【基环树 + 树形DP】

HDU6403 Card Game

题意:

给出\(N\)张卡片,卡片正反两面都有数字,现在要翻转一些卡片使得所有卡片的正面的值各不相同,问最小翻转次数和最小翻转情况下的不同方案数
\(N\le 10^5\)

题解:

首先考虑建图,对于每张卡片,从卡片正面的数字向卡片背面的数字连一条边,那么接下来问题就转化为了:翻转最少的边,使得每个有出度的点的度数为\(1\)(也就是每条边要对应一个点)

对于每个连通块分别考虑

  1. 如果这个连通块中的边比点多,那么必然不存在可行方案,因为肯定有边对应不到点
  2. 如果这个连通块的边和点一样多,那么就构成了一棵基环树,在这个情况下,可以发现在环上的边只要有一条确定好方向,其他所有环上的边都确定了方向了,而不在环上的边的方向必然要指向环,这样才能保证每个点只有一个出度,所以只有环上边的方向不确定,而环上边的方向只有两种,如果两种方向需要翻转的边的数量一样多,那么对于当前连通块的方案数为\(2\),否则为\(1\)
  3. 如果这个连通块的边比点少\(1\),那么就构成一棵树,由于每条边要对应一个点,而点多了一个,所以必然有个点是没有出度的,而其他点都有且仅有一个出度,考虑以任意一个点为根,枚举每个点\(u\),如果这个点作为无出度的点,那么在它的子树中的边必然向上指,而它的祖边(从根节点到当前节点路径上的边)必然向下指,而其他所有边必然会向上指,所以只要计算出来对于每个点它的子树中边的两种方向和祖边的两种方向还有剩下边的两种方向就能确定需要翻转的边的数量了,最后对于这个连通块,找到最小翻转数量和对应的枚举的节点数即可,也可以用换根\(DP\)来做
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e6+7;
typedef long long int LL;
const LL MOD = 998244353;

int n, deg[MAXN], bel[MAXN], ID;
vector<int> G[MAXN],pt[MAXN];
vector<pair<int,int> > es[MAXN];

void init(){
    for(int i = 1; i <= 2 * n; i++) G[i].clear();
    for(int i = 1; i <= 2 * n; i++) deg[i] = 0;
    for(int i = 0; i <= 2 * n; i++) es[i].clear();
    for(int i = 1; i <= 2 * n; i++) bel[i] = 0;
    for(int i = 1; i <= 2 * n; i++) pt[i].clear();
}
void mark(int u, int id){
    bel[u] = id;
    pt[id].push_back(u);
    for(int v : G[u]) if(!bel[v]) mark(v,id);
}
pair<int,LL> circleTree(int id){
    set<int> S;
    for(int &x : pt[id]) S.insert(x);
    map<pair<int,int>,int> msk;
    for(auto &e : es[id]){
        deg[e.first]++, deg[e.second]++;
        msk[e]++;
    }
    queue<int> que;
    for(int &x : pt[id]) if(deg[x]==1){
        S.erase(x);
        que.push(x);
    }
    int __count1 = 0;
    while(!que.empty()){
        int u = que.front(); que.pop();
        for(int v : G[u]){
            deg[v]--;
            if(deg[v]){
                if(msk.count(make_pair(u,v))){
                    msk[make_pair(u,v)]--;
                    if(!msk[make_pair(u,v)]) msk.erase(make_pair(u,v));
                }
                else{
                    __count1++;
                    msk[make_pair(v,u)]--;
                    if(!msk[make_pair(v,u)]) msk.erase(make_pair(v,u));
                }
            }
            if(deg[v]==1){
                S.erase(v);
                que.push(v);
            }
        }
    }
    if(S.size()==1) return make_pair(__count1,1);
    if(S.size()==2){
        pair<int,int> e1(0,0), e2(0,0);
        for(auto &e : es[id]) if(S.count(e.first) and S.count(e.second)){
            if(e1==make_pair(0,0)) e1 = e;
            else e2 = e;
        }
        return e1==e2 ? make_pair(__count1+1,2) : make_pair(__count1,1);
    }
    for(int x : S) G[x].clear();
    vector<pair<int,int> > nowE;
    for(auto &e : es[id]){
        if(S.count(e.first) and S.count(e.second)){
            nowE.push_back(e);
            G[e.first].push_back(e.second);
            G[e.second].push_back(e.first);
        }
    }
    vector<pair<int,int> > circleE;
    int u = *S.begin();
    int pre = -1;
    for(int x : S) deg[x] = 0;
    while(true){
        deg[u] = 1;
        if(G[u][0]==pre) swap(G[u][0],G[u][1]);
        int v = G[u][0];
        circleE.push_back(make_pair(u,v));
        if(deg[v]) break;
        pre = u;
        u = v;
    }
    auto cmp = [](pair<int,int> &A, pair<int,int> &B){
        int mina = min(A.first,A.second), minb = min(B.first,B.second);
        int maxa = max(A.first,A.second), maxb = max(B.first,B.second);
        return mina==minb ? maxa < maxb : mina < minb;
    };
    sort(nowE.begin(),nowE.end(),cmp);
    sort(circleE.begin(),circleE.end(),cmp);
    int __count = 0;
    for(int i = 0; i < (int)nowE.size(); i++){
        if(nowE[i] != circleE[i]) __count++;
    }
    if(__count == (int)S.size() - __count) return make_pair(__count1+__count,2);
    else return make_pair(__count1+min(__count,(int)S.size() - __count),1);
}
map<int,int> mp;
set<pair<int,int> > E;
bool tag[MAXN];
int e1, e2;
void dfs1(int u, int f){
    if(f){
        if(E.count(make_pair(u,f))) e1++, tag[u] = true;
        else e2++, tag[u] = false;
    }
    for(int v : G[u]) if(v!=f) dfs1(v,u);
}
pair<int,int> dfs2(int u, int f, int es1, int es2){
    int re = 0, de = 0;
    for(int v : G[u]){
        if(v==f) continue;
        pair<int,int> R;
        if(tag[v]) R = dfs2(v,u,es1+1,es2);
        else R = dfs2(v,u,es1,es2+1);
        re += R.first; de += R.second;
    }
    mp[de+es1+e2-es2-de] += 1;
    return tag[u] ? make_pair(re+1,de) : make_pair(re,de+1);
}
pair<int,LL> treeDp(int id){
    mp.clear();
    E.clear();
    for(auto &e : es[id]) E.insert(e);
    for(int &x : pt[id]) tag[x] = false;
    e1 = e2 = 0;
    dfs1(pt[id][0],0);
    dfs2(pt[id][0],0,0,0);
    return *mp.begin();
}
void solve(){
    scanf("%d",&n);
    init();
    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);
        es[0].push_back(make_pair(u,v));
    }
    ID = 0;
    for(int i = 1; i <= 2 * n; i++) if(!bel[i]) mark(i,++ID);
    for(auto &e : es[0]) es[bel[e.first]].push_back(e);
    for(int i = 1; i <= ID; i++) if(es[i].size() > pt[i].size()){
        puts("-1 -1");
        return;
    }
    int num = 0;
    LL ret = 1;
    for(int i = 1; i <= ID; i++){
        if(es[i].size() == pt[i].size()){
            auto res = circleTree(i);
            num += res.first;
            ret = ret * res.second % MOD;
        }
        else{
            if(es[i].empty()) continue;
            auto res = treeDp(i);
            num += res.first;
            ret = ret * res.second % MOD;
        }
    }
    printf("%d %I64d\n",num,ret);
}
int main(){
    int tt; for(scanf("%d",&tt); tt; tt--) solve();    
    return 0;
}
posted @ 2020-06-23 02:35  _kiko  阅读(159)  评论(0编辑  收藏  举报