题解 | Checkers-2019牛客暑期多校训练营第九场G题

题目来源于牛客竞赛:https://ac.nowcoder.com/acm/contest/discuss
题目描述:
题解 | Checkers-2019牛客暑期多校训练营第九场G题
输入描述:
题解 | Checkers-2019牛客暑期多校训练营第九场G题
输出描述:
题解 | Checkers-2019牛客暑期多校训练营第九场G题
示例1:
题解 | Checkers-2019牛客暑期多校训练营第九场G题
题解:
题解 | Checkers-2019牛客暑期多校训练营第九场G题
代码:

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
 
using namespace std;
using namespace __gnu_pbds;
  
#define fi first
#define se second
#define mp make_pair
#define pb push_back
  
typedef long long ll;
typedef pair<int,int> ii;
typedef vector<int> vi;
typedef unsigned long long ull;
typedef long double ld;
typedef tree<ii, null_type, less<ii>, rb_tree_tag, tree_order_statistics_node_update> pbds;
 
vector<int> fact;
vector<int> ifact;
vector<int> inv;
vector<int> pow2;
const int MOD = (1e9 + 7);
void radd(int &a, int b)
{
    a+=b;
    while(a>=MOD) a-=MOD;
}
 
int add(int a, int b)
{
    a+=b;
    while(a>=MOD) a-=MOD;
    return a;
}
int mult(int a, int b)
{
    return (a*1LL*b)%MOD;
}
int modpow(int a, int b)
{
    int r=1;
    while(b)
    {
        if(b&1) r=mult(r,a);
        a=mult(a,a);
        b>>=1;
    }
    return r;
}
int inverse(int a)
{
    return modpow(a,MOD-2);
}
void init(int _n)
{
    fact.clear(); ifact.clear(); inv.clear(); pow2.clear();
    fact.resize(_n+1);
    ifact.resize(_n+1);
    inv.resize(_n+1);
    pow2.resize(_n+1);
    pow2[0]=1;
    ifact[0]=1;
    fact[0]=1;
    for(int i=1;i<=_n;i++)
    {
        pow2[i]=add(pow2[i-1],pow2[i-1]);
        fact[i]=mult(fact[i-1],i);
        //ifact[i]=mult(ifact[i-1],inv[i]);
    }
    ifact[_n] = inverse(fact[_n]);
    for(int i=_n-1;i>=1;i--)
    {
        ifact[i] = mult(ifact[i + 1], i + 1);
    }
    for(int i=1;i<=_n;i++)
    {
        inv[i] = mult(fact[i-1],ifact[i]);
    }
}
 
const int M = 12;
const int N = 155;
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
const int C = 4;
 
struct DSU
{
    int S;
    struct node
    {
        int p;
    };
    vector<node> dsu;
    DSU(int n)
    {
        S = n;
        for(int i = 0; i < n; i++)
        {
            node tmp;
            tmp.p = i;
            dsu.pb(tmp);
        }
    }
    int rt(int u)
    {
        if(dsu[u].p == u) return u;
        dsu[u].p = rt(dsu[u].p);
        return dsu[u].p;
    }
    void merge(int u, int v)
    {
        u = rt(u); v = rt(v);
        if(u == v) return ;
        if(rand()&1) swap(u,v);
        dsu[v].p = u;
    }
};
 
map<vi,int> ma;
vi F[555];
const int C2 = 76;
 
vi horizontal_connect(vi conn, int bit)
{
    vi nw(C,0);
    DSU dsu(C*2+3);
    assert(conn.size()==C);
    int cur=C+1;
    for(int i=0;i+1<conn.size();i++)
    {
        if(bit&(1<<i))
        {
            if(conn[i]==0) conn[i]=cur++;
            if(conn[i+1]==0) conn[i+1]=cur++;
            dsu.merge(conn[i],conn[i+1]);
        }
    }
    int ID[C*2+3]; memset(ID,-1,sizeof(ID)); int c=1;
    ID[0]=0;
    for(int i=0;i<conn.size();i++)
    {
        if(conn[i]>0&&(dsu.rt(conn[i])==conn[i])&&ID[conn[i]]==-1)
        {
            ID[conn[i]]=c++;
        }
    }
    for(int i=0;i<conn.size();i++)
    {
        nw[i] = ID[dsu.rt(conn[i])];
    }
    int msiz=ma.size(); assert(msiz<C2);
    if(ma.find(nw)==ma.end()) {ma[nw]=msiz-1; F[msiz-1]=nw;}
    return nw;
}
 
vi vertical_connect(vi conn, int bit)
{
    vi nw(C,0); assert(conn.size()==C); set<int> S; set<int> S2;
    for(int i=0;i<conn.size();i++)
    {
        S2.insert(conn[i]);
        if(bit&(1<<i))
        {
            nw[i]=conn[i]; S.insert(nw[i]);
        }
    }
    S2.erase(0); S.erase(0);
    if(S.size()!=S2.size()) return {};
    int msiz=ma.size(); assert(msiz<C2);
    if(ma.find(nw)==ma.end()) {ma[nw]=msiz-1; F[msiz-1]=nw;}
    return nw;
}
 
int dp[N+11][(1<<C)+1][C2+1][(1<<C)+1];
 
int g(int id)
{
    vi tmp=F[id]; int bit=0;
    for(int i=0;i<C;i++)
    {
        if(tmp[i]>0) bit^=(1<<i);
    }
    return bit;
}
 
int solve_fast2(vector<ii> vec)
{
    int n = vec.size();
    if(vec.empty()) return 0;
    set<ii> S;
    for(int i=0;i<n;i++) S.insert(vec[i]);
    int ans = 0;
    //count odd degree vertices
    for(int i=0;i<N;i+=2)
    {
        for(int j=0;j<M;j+=2)
        {
            int ex=0;
            for(int d=0;d<4;d++)
            {
                if(S.find(mp(i+dx[d],j+dy[d]))!=S.end())
                {
                    ex=1; break;
                }
            }
            if(ex) ans=add(ans,pow2[n-1]);
        }
    }
    //count eulerian cycles
    ma.clear();
    ma[{}]=-1;
    vi emp(C,0); ma[emp]=0; F[0]=emp;
    memset(dp,0,sizeof(dp));
    dp[0][0][0][0]=1;
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<(1<<C);j++)
        {
            for(int k=0;k<C2;k++)
            {
                for(int l=0;l<(1<<C);l++)
                {
                    if(dp[i][j][k][l]==0) continue;
                    int v=dp[i][j][k][l];
                    if(i%2==0)
                    {
                        for(int bit=0;bit<(1<<(C-1));bit++)
                        {
                            bool pos=1; int j2=j;
                            for(int z=0;z<C-1;z++)
                            {
                                //connect (i,2z) with (i,2(z+1)) using (i,2z+1)
                                if((bit&(1<<z))>0&&S.find(mp(i,2*z+1))==S.end())
                                {
                                    pos=0; break;
                                }
                                if(bit&(1<<z)){j2^=(1<<z); j2^=(1<<(z+1));}
                            }
                            if(!pos) continue;
                            vi conn = F[k];
                            vi nw = horizontal_connect(conn, bit);
                            int cnt = 0;
                            //count new bad edges here
                            for(int z=0;z<C;z++)
                            {
                                if(conn[z]==0&&nw[z]>0) //(i,2z) just became bad
                                {
                                    if(S.find(mp(i-1,2*z))!=S.end() && !(l&(1<<z)))
                                    {
                                        cnt++;
                                    }
                                    if(S.find(mp(i+1,2*z))!=S.end())
                                    {
                                        cnt++;
                                    }
                                    //spread to left and right
                                    if(S.find(mp(i,2*z+1))!=S.end()&&conn[z+1]==0)
                                    {
                                        cnt++;
                                    }
                                    if(S.find(mp(i,2*z-1))!=S.end()&&nw[z-1]==0)
                                    {
                                        cnt++;
                                    }
                                }
                            }
                            radd(dp[i+1][j2][ma[nw]][g(k)], mult(v, inv[(1<<cnt)]));
                        }
                    }
                    else
                    {
                        for(int bit=0;bit<(1<<C);bit++)
                        {
                            bool pos=1; int j2=j;
                            for(int z=0;z<C;z++)
                            {
                                //connect (i-1,2z) and (i+1,2z) with (i,2z)
                                if((bit&(1<<z))>0&&S.find(mp(i,2*z))==S.end())
                                {
                                    pos=0; break;
                                }
                                if(bit&(1<<z)){j2^=(1<<z);}
                            }
                            if(!pos) continue;
                            if(j2!=0) continue; //or else no eulerian cycle
                            vi conn = F[k];
                            vi nw = vertical_connect(conn, bit);
                            if(nw.empty()) continue;
                            int cnt=0;
                            //count new bad edges here
                            //impossible for (i-1,2z) to be bad but become good now by parity
                            for(int z=0;z<C;z++)
                            {
                                if(nw[z]>0) //(i+1,2z) just became bad, push to all 4 directions
                                {
                                    //up not needed
                                    if(S.find(mp(i+2,2*z))!=S.end())
                                    {
                                        cnt++;
                                    }
                                    //spread to left and right
                                    if(S.find(mp(i+1,2*z+1))!=S.end())
                                    {
                                        cnt++;
                                    }
                                    if(S.find(mp(i+1,2*z-1))!=S.end()&&nw[z-1]==0)
                                    {
                                        cnt++;
                                    }
                                }
                            }
                            radd(dp[i+1][bit][ma[nw]][g(k)], mult(v, inv[(1<<cnt)]));
                        }
                    }
                }
            }
        }
    }
    vi good;
    for(auto X:ma)
    {
        if(X.fi.empty()) continue;
        int mx=0;
        for(int i=0;i<X.fi.size();i++)
        {
            mx=max(mx,X.fi[i]);
        }
        if(mx==1) good.pb(X.se);
    }
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<(1<<C);j++)
        {
            for(int k:good)
            {
                for(int l=0;l<(1<<C);l++)
                {
                    if(i%2==1&&j==0) ans=add(ans, mult(2, mult(pow2[n], dp[i][j][k][l])));
                }
            }
        }
    }
    ans = mult(ans, inv[2]);
    return ans;
}
 
int solve_fast(vector<ii> vec)
{
    int n = vec.size();
    vector<ii> V[2];
    for(int i=0;i<n;i++)
    {
        if((vec[i].fi+vec[i].se)%2==0)
        {
            vec[i].se++;
            V[0].pb(vec[i]);
        }
        else
        {
            V[1].pb(vec[i]);
        }
    }
    int ans = 0;
    for(int i=0;i<2;i++)
    {
        int sol = solve_fast2(V[i]);
        ans = add(ans, mult(pow2[int(V[i^1].size())], sol));
    }
    return ans;
}
 
void read_solve()
{
    int n; cin>>n;
    vector<ii> vec;
    for(int i=0;i<n;i++)
    {
        int x,y; cin>>x>>y;
        vec.pb(mp(x,y));
    }
    cout<<solve_fast(vec)<<'\n';
}
 
int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0);
    init(255555);
    read_solve();
}

更多问题,更详细题解可关注牛客竞赛区,一个刷题、比赛、分享的社区。
传送门:https://ac.nowcoder.com/acm/contest/discuss

上一篇:树上启发式合并与dsu on tree


下一篇:【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)