The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解

Problem A  Right-Coupled Numbers

温暖的签到题。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        int n ;
        cin >> n ;
        int flag = 0 ;
        for(int i = 1 ; i * i <= n ; i ++)
        {
            if(n % i == 0)
            {
                int j = n / i ;
                if(min(i , j) * 2 >= max(i , j))  flag = 1 ;
            }
        }
        cout << flag << '\n' ;
    }
    return 0 ;
}

 

Problem B  Make Numbers

温暖的签到题。

#include<bits/stdc++.h>
using namespace std ;
set<int> s ;
int n , a[10] ;
int p[10] ;
void cal1(int l1 , int r1 , int l2 , int r2)
{
    int res1 = 0 ;
    for(int i = l1 ; i <= r1 ; i ++)  res1 = res1 * 10 + a[p[i]] ;
    int res2 = 0 ;
    for(int i = l2 ; i <= r2 ; i ++)  res2 = res2 * 10 + a[p[i]] ;
    if(res1 + res2 >= 0)  s.insert(res1 + res2) ;
    if(res1 - res2 >= 0)  s.insert(res1 - res2) ;
    if(res1 * res2 >= 0)  s.insert(res1 * res2) ;
}
void cal2(int l1 , int r1 , int l2 , int r2 , int l3 , int r3)
{
    int res1 = 0 ;
    for(int i = l1 ; i <= r1 ; i ++)  res1 = res1 * 10 + a[p[i]] ;
    int res2 = 0 ;
    for(int i = l2 ; i <= r2 ; i ++)  res2 = res2 * 10 + a[p[i]] ;
    int res3 = 0 ;
    for(int i = l3 ; i <= r3 ; i ++)  res3 = res3 * 10 + a[p[i]] ;
    if(res1 + res2 + res3 >= 0)  s.insert(res1 + res2 + res3) ;
    if(res1 + res2 - res3 >= 0)  s.insert(res1 + res2 - res3) ;
    if(res1 + res2 * res3 >= 0)  s.insert(res1 + res2 * res3) ;
    if(res1 - res2 + res3 >= 0)  s.insert(res1 - res2 + res3) ;
    if(res1 - res2 - res3 >= 0)  s.insert(res1 - res2 - res3) ;
    if(res1 - res2 * res3 >= 0)  s.insert(res1 - res2 * res3) ;
    if(res1 * res2 + res3 >= 0)  s.insert(res1 * res2 + res3) ;
    if(res1 * res2 - res3 >= 0)  s.insert(res1 * res2 - res3) ;
    if(res1 * res2 * res3 >= 0)  s.insert(res1 * res2 * res3) ;
}
void cal3()
{
    int res1 = a[p[1]] ;
    int res2 = a[p[2]] ;
    int res3 = a[p[3]] ;
    int res4 = a[p[4]] ;
    if(res1 + res2 + res3 + res4 >= 0)  s.insert(res1 + res2 + res3 + res4) ;
    if(res1 + res2 - res3 + res4 >= 0)  s.insert(res1 + res2 - res3 + res4) ;
    if(res1 + res2 * res3 + res4 >= 0)  s.insert(res1 + res2 * res3 + res4) ;
    if(res1 - res2 + res3 + res4 >= 0)  s.insert(res1 - res2 + res3 + res4) ;
    if(res1 - res2 - res3 + res4 >= 0)  s.insert(res1 - res2 - res3 + res4) ;
    if(res1 - res2 * res3 + res4 >= 0)  s.insert(res1 - res2 * res3 + res4) ;
    if(res1 * res2 + res3 + res4 >= 0)  s.insert(res1 * res2 + res3 + res4) ;
    if(res1 * res2 - res3 + res4 >= 0)  s.insert(res1 * res2 - res3 + res4) ;
    if(res1 * res2 * res3 + res4 >= 0)  s.insert(res1 * res2 * res3 + res4) ;

    if(res1 + res2 + res3 - res4 >= 0)  s.insert(res1 + res2 + res3 - res4) ;
    if(res1 + res2 - res3 - res4 >= 0)  s.insert(res1 + res2 - res3 - res4) ;
    if(res1 + res2 * res3 - res4 >= 0)  s.insert(res1 + res2 * res3 - res4) ;
    if(res1 - res2 + res3 - res4 >= 0)  s.insert(res1 - res2 + res3 - res4) ;
    if(res1 - res2 - res3 - res4 >= 0)  s.insert(res1 - res2 - res3 - res4) ;
    if(res1 - res2 * res3 - res4 >= 0)  s.insert(res1 - res2 * res3 - res4) ;
    if(res1 * res2 + res3 - res4 >= 0)  s.insert(res1 * res2 + res3 - res4) ;
    if(res1 * res2 - res3 - res4 >= 0)  s.insert(res1 * res2 - res3 - res4) ;
    if(res1 * res2 * res3 - res4 >= 0)  s.insert(res1 * res2 * res3 - res4) ;

    if(res1 + res2 + res3 * res4 >= 0)  s.insert(res1 + res2 + res3 * res4) ;
    if(res1 + res2 - res3 * res4 >= 0)  s.insert(res1 + res2 - res3 * res4) ;
    if(res1 + res2 * res3 * res4 >= 0)  s.insert(res1 + res2 * res3 * res4) ;
    if(res1 - res2 + res3 * res4 >= 0)  s.insert(res1 - res2 + res3 * res4) ;
    if(res1 - res2 - res3 * res4 >= 0)  s.insert(res1 - res2 - res3 * res4) ;
    if(res1 - res2 * res3 * res4 >= 0)  s.insert(res1 - res2 * res3 * res4) ;
    if(res1 * res2 + res3 * res4 >= 0)  s.insert(res1 * res2 + res3 * res4) ;
    if(res1 * res2 - res3 * res4 >= 0)  s.insert(res1 * res2 - res3 * res4) ;
    if(res1 * res2 * res3 * res4 >= 0)  s.insert(res1 * res2 * res3 * res4) ;
}
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    for(int i = 1 ; i <= 4 ; i ++)  cin >> a[i] ;
    for(int i = 1 ; i <= 4 ; i ++)  p[i] = i ;
    do
    {
        for(int i = 1 ; i <= 3 ; i ++)  cal1(1 , i , i + 1 , 4) ;
        for(int i = 1 ; i <= 4 ; i ++)  for(int j = i + 1 ; j <= 4 ; j ++)  
            if(i + 1 <= j && j + 1 <= 4)
              cal2(1 , i , i + 1 , j , j + 1 , 4) ;
        cal3() ;
    } while(next_permutation(p + 1 , p + 4 + 1)) ;
    cout << s.size() << '\n' ;
    return 0 ;

}

 

Problem C  Pyramid

考虑前The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解个小球经过后每个节点的开关状态,也就是每个节点有多少个小球经过。

The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解表示第The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解层第The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解个节点有多少个小球经过。

转移方程The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解

在知道开关状态后就可以模拟第The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解个小球的移动过程了。

#include<bits/stdc++.h>
using namespace std ;
const int maxn = 10000 + 10 ;
int dp[2][maxn] ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        int n , k ;
        cin >> n >> k ;
        if(n == 1)
        {
            cout << "0\n" ;
            continue ;
        }
        n -- ;
        memset(dp , 0 , sizeof(dp)) ;
        int now = 0 ;
        dp[0][0] = k - 1 ;
        for(int i = 0 ; i < n ; i ++)
        {
            memset(dp[1 - i % 2] , 0 , sizeof(dp[1 - i % 2])) ;
            for(int j = 0 ; j <= i ; j ++)
            {
                dp[1 - i % 2][j] += (dp[i % 2][j] + 1) / 2 ;
                dp[1 - i % 2][j + 1] += dp[i % 2][j] / 2 ;
            }
            if(dp[i % 2][now] % 2 == 1)  now ++ ;
        }
        cout << now << '\n' ;
    }
    return 0 ;
}

 

Problem D  Quality Monitoring

留坑。

 

Problem E  A Color Game

The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解表示The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解这段区间内只剩下第The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解种颜色时第The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解种颜色的个数最大值。

The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解表示The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解这段区间可以全部清除。

转移就套区间The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解板子即可。颜色相同的两个区间才可以合并。

#include<bits/stdc++.h>
using namespace std ;
const int maxn = 500 + 10 ;
int n , m ;
char s[maxn] ;
int a[maxn] ;
int dp[maxn][maxn][7] ;
bool ok[maxn][maxn] ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    cin >> s + 1 >> m ;
    n = strlen(s + 1) ;
    for(int i = 1 ; i <= n ; i ++)
    {
        if(s[i] == 'R')  a[i] = 0 ;
        else if(s[i] == 'G')  a[i] = 1 ;
        else if(s[i] == 'B')  a[i] = 2 ;
        else if(s[i] == 'C')  a[i] = 3 ;
        else if(s[i] == 'M')  a[i] = 4 ;
        else if(s[i] == 'Y')  a[i] = 5 ;
        else  a[i] = 6 ;
    }
    if(m == 1)
    {
        cout << "Yes\n" ;
        return 0 ;
    }
    for(int i = 1 ; i <= n ; i ++)  
        for(int j = i ; j <= n ; j ++)
            for(int k = 0 ; k < 7 ; k ++)
                dp[i][j][k] = -1e9 ;
    for(int i = 1 ; i <= n ; i ++)  dp[i][i][a[i]] = 1 ;
    for(int len = 2 ; len <= n ; len ++)
        for(int i = 1 ; i + len - 1 <= n ; i ++)
        {
            int j = i + len - 1 ;
            for(int k = i ; k + 1 <= j ; k ++)
            {
                for(int c = 0 ; c < 7 ; c ++)
                {
                    if(dp[i][k][c] > 0 && dp[k + 1][j][c] > 0) 
                        dp[i][j][c] = max(dp[i][j][c] , dp[i][k][c] + dp[k + 1][j][c]) ;
                    if(ok[i][k])
                        dp[i][j][c] = max(dp[i][j][c] , dp[k + 1][j][c]) ;
                    if(ok[k + 1][j])
                        dp[i][j][c] = max(dp[i][j][c] , dp[i][k][c]) ;
                }
    
            }
            for(int c = 0 ; c < 7 ; c ++)
                if(dp[i][j][c] >= m)  ok[i][j] = true ;
        }
    if(ok[1][n])  cout << "Yes\n" ;
    else  cout << "No\n" ;
    return 0 ;
}

 

Problem F  Cable Protection

求基环树的最小点覆盖

先考虑树的情况,树型dp即可

The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解

The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解

考虑环上的一条边The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解,则u和v至少有一个点要选,那么我们钦定u必选然后断掉边The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解跑一遍树型dp,再钦定v必选跑一遍树型dp,两者取优即可

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, m, dp[N][2];
vector<int> G[N];
void dfs(int u, int fa, int rt)
{
    int sum1 = 0, summn = 0;
    for(int v : G[u])
    {
        if(v==fa||v==rt) continue;
        dfs(v, u, rt);
        sum1 += dp[v][1], summn += min(dp[v][0], dp[v][1]);
    }
    dp[u][0] = sum1;
    dp[u][1] = summn + 1;
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n+m; i++)
    {
        int u, v; scanf("%d%d", &u, &v);
        ++u, ++v;
        G[u].push_back(v), G[v].push_back(u);
    }
    int ans = 0;
    dfs(1, n, 1);
    ans = dp[1][1];
    dfs(n, 1, n);
    ans = min(ans, dp[n][1]);
    printf("%d\n", ans);
    return 0;
}

 

Problem G  Graph Cards

判定基环树同构

先找到环,那么对于环上的每个点的子树做一遍树哈希,问题就转化为了判断两个环旋转同构。

利用最小表示法,把环变成链后再哈希即可(需要正反各做一遍)

 

树哈希的方法有很多

1. The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解

2. The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解

3. The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解

第一种需要先按The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解排序,第二三种不需要。

本题数据较强,23需要一起用才能过

 

链哈希,就直接让The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解,由于树哈希后值都很大,可以利用map离散化成小数,可以降低冲突率

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
using pii = pair<int, int>;
const int N = 1e6 + 5, mod = 998244353, mod2 = 1e9 + 7, base = 19260817;
map<pii, int> lsh;
set<pii> st;
int tot;
int pr[N];
void getp(int n)
{
    static bool vis[N];
    for(int i=2; i<=n; i++)
    {
        if(!vis[i]) pr[++pr[0]] = i;
        for(int j=1; j<=pr[0]&&1ll*pr[j]*i<=n; j++) 
        {
            vis[pr[j]*i] = 1; 
            if(i%pr[j]==0) break;
        } 
    }
}
int minshift(vector<int> &s)
{
    int n = s.size();
    int i = 0, j = 1, k = 0;
    while(i<n&&j<n&&k<n)
    {
        int t = s[(i+k)%n] - s[(j+k)%n];
        if(!t) k++;
        else
        {                          
            if(t>0) i += k + 1;   
            else j += k + 1;    
            if(i==j) j++;
            k = 0;
        }
    }
    return i < j ? i : j; 
}
struct Tree
{
    vector<int> G[N], loop;
    bool vis[N];
    int n, in[N], sz[N];
    pii hval[N];
    void init()
    {
        scanf("%d", &n);
        for(int i=1; i<=n; i++) G[i].clear(), in[i] = vis[i] = 0;
        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);
            ++in[u], ++in[v];
        }
    }
    void findloop()
    {
        queue<int> q;
        for(int i=1; i<=n; i++) if(in[i]==1) q.push(i);
        while(!q.empty())
        {
            int u = q.front(); q.pop();
            for(int v : G[u])
            {
                --in[v];
                if(in[v]==1) q.push(v);
            }
        }
        loop.clear();
        int st = 0;
        for(int i=1; i<=n; i++) 
            if(in[i]==2) 
            {
                st = i;
                break;
            }
        while(!vis[st])
        {
            loop.push_back(st);
            vis[st] = 1;
            for(int v : G[st])
                if(in[v]==2&&!vis[v])
                {
                    st = v;
                    break;
                }
        }
    }
    void dfs(int u, int fa)
    {
        hval[u].fi = hval[u].se = sz[u] = 1;
        for(int v : G[u])
            if(v!=fa&&!vis[v]) 
            {
                dfs(v, u);
                sz[u] += sz[v];
                hval[u].fi = (hval[u].fi + 1ll*hval[v].fi*pr[sz[v]]%mod)%mod;
                hval[u].se = 1ll*hval[u].se*(hval[v].se + pr[sz[v]])%mod2;
            }
    } 
    void geth()
    {
        findloop();
        for(int &x : loop)
        {
            dfs(x, 0);
            auto hv = hval[x];
            if(!lsh.count(hv)) lsh[hv] = ++tot;
            x = lsh[hv];
        }
        int idx = minshift(loop), len = loop.size();
        int hsh1 = 0;
        for(int i=0; i<len; i++)
            hsh1 = (1ll*hsh1*base + loop[(idx+i)%len])%mod;
        int hsh2 = 0;
        reverse(begin(loop), end(loop));
        idx = minshift(loop);
        for(int i=0; i<len; i++)
            hsh2 = (1ll*hsh2*base + loop[(idx+i)%len])%mod;
        if(hsh1>hsh2) swap(hsh1, hsh2);
        st.insert({hsh1, hsh2});
    }
}t;
void solve()
{
    lsh.clear(); tot = 0;
    st.clear();
    int cnt; scanf("%d", &cnt);
    for(int i=1; i<=cnt; i++) 
    {
        t.init();
        t.geth();
    }
    printf("%d\n", (int)st.size());
}
int main()
{
    getp(N-1); 
    int _; scanf("%d", &_);
    while(_--) solve();
    return 0;
}

 

 

Problem H  Optimization for UltraNet

在无向带权图上,求一棵生成树,在最大化最小边权的前提下,最小化The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest 部分题解,其中$f(i,j)$定义为i和j路径上边权最小的边

先求一棵最大生成树,那么我们该树中的最小边权就是结果的最小边权

之后要满足第二个条件,直觉就是边权越小越好,所以从该条边权开始再求一遍最小生成树即可

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e5 + 5;
struct edge
{
    int u, v, w;
    bool operator < (const edge& oth) { return w > oth.w; }
}e[N], tr[N];
int n, m;
int fa[N], sz[N];
void init(int n) 
{ 
    iota(fa+1, fa+n+1, 1); 
    for(int i=1; i<=n; i++) sz[i] = 1;
}
int find(int x) { return x==fa[x] ? x : fa[x] = find(fa[x]); }
bool merge(int x, int y)
{
    int fx = find(x), fy = find(y);
    if(fx!=fy) 
    {
        fa[fx] = fy;
        sz[fy] += sz[fx];
        return true;
    }
    return false;
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
    sort(e+1, e+m+1);
    int cnt = 0;
    init(n);
    for(int i=1; i<=m; i++)
    {
        cnt += merge(e[i].u, e[i].v);
        if(cnt==n-1)
        {
            init(n);
            cnt = 0;
            for(int j=i; j>=1; j--) if(merge(e[j].u, e[j].v)) tr[++cnt] = e[j];
            break;
        }
    }
    reverse(tr+1, tr+cnt+1);
    init(n);
    ll ans = 0;
    for(int i=1; i<=cnt; i++)
    {
        ans += 1ll*sz[find(tr[i].u)]*sz[find(tr[i].v)]*tr[i].w;
        merge(tr[i].u, tr[i].v);
    }
    printf("%lld\n", ans);
    return 0;
}

 

 

Problem I  Critical Structures

题目难度主要在读题上,因为对连通图那一套概念忘完了。所以题目一直读不懂。

给你一张无向图。问你割点数量、割边数量、点双个数、点双的边的数量的最大值。

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 1000 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
#include<bits/stdc++.h>
using namespace std ;
const int maxm = 2e6 + 10 ;
int n , m ;
int head[maxn] ;
int cnt = 1 ;
int dfn[maxn] , low[maxn] , parent[maxn] ;
int ans = 0 ;
bool cut[maxn] ;
int ans1 , ans2 , ans3 , ans4 ;
bool ok[maxn][maxn] ;
bool vis[maxn] ;
int c[maxn] ;
vector<pair<int , int>> tmp ;
int res ;
int num[maxn] ;
int stk[maxn] ;
int top ;
vector<int> vcc ;
bool used[maxn] ;
struct Node
{
    int v , next ;
} edge[maxm] ;
void add(int u , int v)
{
    edge[cnt].v = v ;
    edge[cnt].next = head[u] ;
    head[u] = cnt ++ ;
}
void update()
{
    for(auto u : vcc)  used[u] = true ;
    int res = 0 ;
    for(auto u : vcc)  for(int i = head[u] ; i != 0 ; i = edge[i].next)
    {
        int v = edge[i].v ;
        if(used[v])  res ++ ;
    }
    res /= 2 ;
    ans4 = max(ans4 , res) ;
    for(auto u : vcc)  used[u] = false ;
}
void dfs(int u)
{
    static int count = 0 ;
    int i , j ;
    int children = 0 ;
    int v ;
    dfn[u] = low[u] = ++ count ;
    for (i = head[u] ; i != 0 ; i = edge[i].next)
    {
        v = edge[i].v ;
        if (dfn[v] == 0)
        {
            stk[++top]=edge[i].v;
            children ++ ;
            parent[v] = u ;
            dfs(v) ;
            low[u] = min(low[u] , low[v]) ;
            if(cut[u] == false && (parent[u] == -1 && children >= 2 || parent[u] != -1 && low[v] >= dfn[u]))
                cut[u] = true , ans1 ++ ;
            if(low[v] > dfn[u])  tmp.push_back({u , v}) , ok[u][v] = true , ans2 ++ ;   
            if(low[v] >= dfn[u])  
            {
                ans3 ++ ;
                vcc.clear() ;
                while(stk[top]!=edge[i].v)//将点出栈直到目标点 
                     vcc.push_back(stk[top--]);
                 vcc.push_back(stk[top--]);//目标点出栈 
                 vcc.push_back(u);//不要忘了将当前点存入bcc  
                update() ;       
            }
        }
        else if (v != parent[u])
        low[u] = min(low[u], dfn[v]) ;
    }
}
// void dfs2(int u)
// {
//     vis[u] = true ;
//     c[u] = ans3 ;
//     for(int i = head[u] ; i != 0 ; i = edge[i].next)
//     {
//         int v = edge[i].v ;
//         if(!vis[v] && !ok[u][v] && !ok[v][u])  
//             dfs2(v) ;
//     }
// }
int main()
{
    ios ;
    int T ;
    cin >> T ;
    while(T --)
    {
        cin >> n >> m ;
        memset(head , 0 , sizeof(head)) ;
        memset(parent , -1 , sizeof(parent)) ;
        memset(dfn , 0 , sizeof(dfn)) ;
        memset(cut , false , sizeof(cut)) ;
        memset(vis , false , sizeof(vis)) ;
        memset(c , 0 , sizeof(c)) ;
        memset(num , 0 , sizeof(num)) ;
        for(int i = 1 ; i <= m ; i ++)
        {
            int u , v ;
            cin >> u >> v ;
            add(u , v) ;
            add(v , u) ;
        }
        ans1 = 0 , ans2 = 0 ;
        ans3 = 0 , ans4 = 0 ;
        for(auto now : tmp)  ok[now.first][now.second] = false ;
        tmp.clear() ;
        for(int i = 1 ; i <= n ; i ++)  if(dfn[i] == 0)  stk[top=1]=i,dfs(i) ;
        //cout << ans3 << '\n' ;
        // for(int i = 1 ; i <= n ; i ++)  
        //     if(!vis[i])  
        //        ans3 ++ , dfs2(i) ;
        // for(int i = 1 ; i <= n ; i ++)
        //     for(int j = head[i] ; j != 0 ; j = edge[j].next)
        //         if(c[i] == c[edge[j].v])
        //             num[c[i]] ++ ;
        //for(int i = 1 ; i <= ans3 ; i ++)  ans4 = max(ans4 , num[i]) ;
        //cout << "??? " << tmp.size() << '\n' ;
        //ans3 += ans2 ;
        //ans4 /= 2 ;
        int gcd = __gcd(ans3 , ans4) ;
        ans3 /= gcd ;
        ans4 /= gcd ;
        cout << ans1 << ' ' << ans2 << ' ' << ans3 << ' ' << ans4 << '\n' ;
    }
    return 0 ;
}

 

Problem J  Puzzle Game

留坑。

 

Problem K  Number with Bachelors

定义一种数字是好的,当且仅当各个数位两两不同(无前导0)

现在问你L到R之间好的数字的个数,或问你第x个好的数字是什么

并且询问有可能是10进制,也可能是16进制

相当于有4个子问题。

先解决0到x范围内有多少个好的数字:考虑到数字种类数很少,所以状压一下即可。

那么第一种问题直接差分一下,第二种问题就从高位到低位,从小到大枚举当前位置的数,然后求出以当前为前缀时的方案数,超过了x就填。

16进制的处理,可以直接用%llx进行scanf和printf,不放心就手写下进制转化

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
char s[105], op[2], ans[105];
bool dflag;
int type;
ull dp[2][20][1<<16];
int convert(char c)
{
    if(isdigit(c)) return c - '0';
    return c - 'a' + 10;
}
char convert2(int x)
{
    if(x<=9) return char('0' + x);
    return char('a' + x - 10);
}
ull conv10(char *s)
{
    ull x = 0;
    int n = strlen(s);
    for(int i=0; i<n; i++) x = x*10 + convert(s[i]);
    return x;
}
ull conv16(char *s)
{
    ull x = 0;
    int n = strlen(s);
    for(int i=0; i<n; i++) x = x*16 + convert(s[i]);
    return x;
}
void print16(ull x)
{
    int tp = 0;
    static char stk[105];
    if(!x) puts("0");
    else
    {
        while(x)
        {
            stk[++tp] = convert2(x%16);
            x /= 16;
        }
        for(int i=tp; i>=1; i--) putchar(stk[i]);
        puts("");
    }
}
ull DP(int b, int lim, int z, int st)
{
    if(b<0) return 1;
    auto &x = dp[dflag][b][st];
    if(!lim&&!z&&~x) return x;
    int up = lim ? convert(s[b]) : (dflag ? 9 : 15);
    ull ans = 0;
    for(int i=0; i<=up; i++)
    {
        if(z&&!i) ans += DP(b-1, lim&&i==up, z, st);
        else
        {
            if((st>>i)&1) continue;
            ans += DP(b-1, lim&&i==up, 0, st|(1<<i));
        }
    }
    if(!lim&&!z) x = ans;
    return ans;
}
ull gao(char *s)
{
    int n = strlen(s);
    reverse(s, s+n);
    if(dflag && n>10) return DP(9, 0, 1, 0);
    if(!dflag && n>16) return DP(15, 0, 1, 0);
    return DP(n-1, 1, 1, 0);
}
int check(char *s)
{
    int st = 0, n = strlen(s);
    for(int i=0; i<n; i++)
    {
        int d = convert(s[i]);
        if((st>>d)&1) return 0;
        st |= (1<<d);
    }
    return 1;
}
void Try(int b, int z, int st, ull rk)
{
    if(b<0) 
    {
        int st = (dflag ? 9 : 15);
        while(st && !ans[st]) --st;
        for(int i=st; i>=0; i--) putchar(convert2(ans[i]));
        puts("");
        return;
    }
    int up = dflag ? 9 : 15;
    for(int i=0; i<=up; i++)
    {
        ans[b] = i;
        if(z&&!i) 
        {
            ull cnt = DP(b-1, 0, z, st);
            if(cnt<rk) rk -= cnt;
            else
            {
                Try(b-1, z, st, rk);
                return;
            }
        }
        else
        {
            if((st>>i)&1) continue;
            ull cnt = DP(b-1, 0, 0, st|(1<<i));
            if(cnt<rk) rk -= cnt;
            else
            {
                Try(b-1, 0, st|(1<<i), rk);
                return;
            }
        }
    }
    puts("-");
}
void solve()
{
    scanf("%s%d", op, &type);
    if(op[0]=='d') dflag = 1;
    else dflag = 0;
    if(!type)
    {
        scanf("%s", s);
        ull L = gao(s) - check(s);
        scanf("%s", s);
        ull R = gao(s);
        if(dflag) printf("%llu\n", R - L);
        else print16(R-L);
    }
    else
    {
        scanf("%s", s);
        if(dflag) Try(9, 1, 0, conv10(s));
        else Try(15, 1, 0, conv16(s));
    }
}
int main()
{
    memset(dp, -1, sizeof(dp));
    int _; scanf("%d", &_);
    while(_--) solve();
    return 0;
}
/*
6
d 0 10 20
h 0 10 1f
d 1 10
h 1 f
d 1 1000000000
h 1 ffffffffffffffff
*/

 

Problem L  Save lives or money

留坑。

 

Problem M  Keystroke

温暖的签到题。

#include<bits/stdc++.h>
using namespace std ;
int row[] = {0 , 0 , 1 , 1} ;
int col[] = {0 , 1 , 0 , 1} ; 
bool c[2] ;
bool d[2] ;
bool tc[2] ;
bool td[2] ;
int a[10] ;
int res ;
void dfs(int now)
{
    //a[1] = 1 , a[3] = 1 ;
    if(now > 4)
    {
        tc[0] = tc[1] = td[0] = td[1] = false ;
        for(int i = 1 ; i <= 4 ; i ++)
            if(a[i] == 1)
            {
                tc[(i - 1) / 2] = true ;
                td[(i - 1) % 2] = true ;
            }
        if(c[0] == tc[0] && c[1] == tc[1] && d[0] == td[0] && d[1] == td[1])  res ++ ;
        return ;
    }
    
    for(int i = 0 ; i < 2 ; i ++)  a[now] = i , dfs(now + 1) ;
}
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        int n , m ;
        cin >> n >> m ;
        c[0] = c[1] = d[0] = d[1] = false ;
        for(int i = 1 ; i <= n ; i ++)
        {
            int x ;
            cin >> x ;
            c[x] = true ;
        }
        for(int i = 1 ; i <= m ; i ++)
        {
            int x ;
            cin >> x ;
            d[x] = true ;
        }
        res = 0 ;
        dfs(1) ;
        cout << res << '\n' ;
    }
    return 0 ;
}

 

 

 

上一篇:Sumitomo Mitsui Trust Bank Programming Contest 2019


下一篇:redis中 ll2string() 和 string2ll() 的实现