zoukankan      html  css  js  c++  java
  • PKUSC 2018 题解

    PKUSC 2018 题解

    Day 1

    T1 真实排名

    Solution

    考虑对于每一个人单独算

    每一个人有两种情况,翻倍和不翻倍,他的名次不变等价于大于等于他的人数不变

    设当前考虑的人的成绩为 (v)

    翻倍的话,要求成绩在 ([v, 2v-1]) 的人全部翻倍,剩下的随便

    统计一下这段区间的人数,组合数算一下即可

    不翻倍的话,要求成绩在 ([frac {v+1} 2,v-1]) 的人不翻倍,因为他们如果翻倍就超过了当前这个人

    所以同样统计一下,加上组合数即可

    注意成绩为零得特殊处理,因为他翻不翻倍是一样的

    Code
    // Copyright lzt
    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef std::pair<int, int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef std::pair<long long, long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i, j, k)  for (register int i = (int)(j); i <= (int)(k); i++)
    #define rrep(i, j, k) for (register int i = (int)(j); i >= (int)(k); i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    inline ll read() {
      ll x = 0, f = 1;
      char ch = getchar();
      while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
      }
      while (ch <= '9' && ch >= '0') {
        x = 10 * x + ch - '0';
        ch = getchar();
      }
      return x * f;
    }
    
    const int mod = 998244353;
    const int maxn = 100100;
    int n, k;
    pii p[maxn];
    int ans[maxn], fac[maxn], inv[maxn];
    
    inline int ksm(int x, int p) {
      int ret = 1;
      while (p) {
        if (p & 1) ret = ret * 1ll * x % mod;
        x = x * 1ll * x % mod; p >>= 1;
      }
      return ret;
    }
    inline int C(int x, int y) {
      return fac[x] * 1ll * inv[y] % mod * inv[x - y] % mod;
    }
    inline int calc(int v) {
      int l = 1, r = n;
      while (l + 1 < r) {
        int md = (l + r) >> 1;
        if (p[md].fi > v) r = md - 1;
        else l = md;
      }
      while (p[l].fi <= v && l <= n) l++;
      return l - 1;
    }
    inline int calc(int L, int R) {
      int ret = calc(R) - calc(L - 1);
      return ret;
    }
    
    void work() {
      n = read(), k = read();
      fac[0] = 1; rep(i, 1, n) fac[i] = fac[i - 1] * 1ll * i % mod;
      inv[n] = ksm(fac[n], mod - 2); rrep(i, n - 1, 0) inv[i] = inv[i + 1] * 1ll * (i + 1) % mod;
      rep(i, 1, n) p[i].fi = read(), p[i].se = i;
      sort(p + 1, p + n + 1);
      rep(i, 1, n) {
        if (p[i].fi == 0) {
          ans[p[i].se] = C(n, k);
          continue;
        }
        int sum = 0;
        int num = calc(p[i].fi, p[i].fi * 2 - 1);
        if (num <= k) sum = sum + C(n - num, k - num);
        num = calc((p[i].fi + 1) / 2, p[i].fi - 1) + 1;
        if (n - num >= k) sum = sum + C(n - num, k);
        ans[p[i].se] = sum % mod;
      }
      rep(i, 1, n) printf("%d
    ", ans[i]); puts("");
    }
    
    int main() {
      #ifdef LZT
        freopen("in", "r", stdin);
        freopen("out", "w", stdout);
      #endif
    
      work();
    
      #ifdef LZT
        Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
      #endif
    }
    

    T2 最大前缀和

    Solution

    考虑每一个子集的贡献

    事实上,一个序列能够成为最大前缀和当且仅当满足两个条件:

    1. 这个序列的任意一个后缀和大于等于零(所以不能截取出某个小前缀的和比它大)
    2. 剩下的序列的任意一个前缀和小于零(所以不能加上某一段之后和比他大)

    这两部分都可以 ( ext{dp}) 出来

    我们令 (f[mask]) 表示选了 (mask) 对应的数,任意一个后缀和大于等于零的排列数,(g[mask]) 表示选了 (mask) 对应的数,任意一个前缀和小于零的排列数

    那么一个 (mask) 的贡献就是 (f[mask] cdot g[mx - mask] cdot sum[mask])

    ( ext{dp}) 的过程详见代码,很简单的一个 ( ext{dp}),注意两个 ( ext{dp}) 转移的方向不同

    Code
    // Copyright lzt
    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef std::pair<int, int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef std::pair<long long, long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i, j, k)  for (register int i = (int)(j); i <= (int)(k); i++)
    #define rrep(i, j, k) for (register int i = (int)(j); i >= (int)(k); i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    inline ll read() {
      ll x = 0, f = 1;
      char ch = getchar();
      while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
      }
      while (ch <= '9' && ch >= '0') {
        x = 10 * x + ch - '0';
        ch = getchar();
      }
      return x * f;
    }
    
    const int mod = 998244353;
    const int maxn = 22;
    const int mxst = 1100000;
    int n;
    int a[maxn];
    ll s[mxst];
    int f1[mxst], f2[mxst];
    // f1[s] 表示 s 中的数排列使得任意一个前缀和<0 的方案数
    // f2[s] 表示 s 中的数排列使得任意一个后缀和>=0 的方案数
    
    inline void add(int &x, int y) {
      x += y;
      if (x >= mod) x -= mod;
      if (x < 0) x += mod;
    }
    
    void work() {
      n = read(); int mx = (1 << n) - 1;
      rep(i, 0, n - 1) a[i] = read();
      rep(i, 0, mx) rep(j, 0, n - 1) if ((i >> j) & 1) s[i] += a[j];
      f1[0] = 1;
      rep(i, 0, mx) {
        if (s[i] >= 0) continue;
        rep(j, 0, n - 1) {
          if ((i >> j) & 1)
            add(f1[i], f1[i - (1 << j)]);
        }
      }
      f2[0] = 1;
      rep(i, 0, mx) {
        if (s[i] < 0) continue;
        rep(j, 0, n - 1) {
          if ((i >> j) & 1) continue;
          add(f2[i | (1 << j)], f2[i]);
        }
      }
      int ans = 0;
      rep(i, 0, mx) {
        int j = mx - i;
        add(ans, f2[i] * 1ll * f1[j] % mod * s[i] % mod);
      }
      printf("%d
    ", ans);
    }
    
    int main() {
      #ifdef LZT
        freopen("in", "r", stdin);
      #endif
    
      work();
    
      #ifdef LZT
        Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
      #endif
    }
    

    T3 主斗地

    Solution

    大力搜索

    首先发现状态数不多,大概在 (3^{12} imes 2 imes 2=2e6) 和 $4^{12} imes 2 imes 2=6e7 $之间,估计就是 (1e7) 这个级别的

    那么我们需要快速判断一个状态是否可行

    还是大力搜索

    首先学会一个技能叫做拆单牌

    我们发现除了飞机,四带二,三带一和三带二以外的牌型,都可以直接拆成数张单牌一一打出,不影响结果

    比如对子可以变成两张单牌,顺子可以变成一溜单牌,并且每张单牌和对家拆出来的单牌大小关系都一样

    所以关键在于暴力搜三和四

    然后枚举三带了几个二,因为这个二的大小不影响结果,所以我们尽量取jry的大的二和网友的小的二配对

    剩下的看看能带走几个不影响结果的单牌,也是jry的大单牌和网友的小单牌

    最后单牌看看能不能压住即可

    Code
    // by sigongzi
    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define pdi pair<db,int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 100005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
        }
        while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
        out(x / 10);
        }
        putchar('0' + x % 10);
    }
    char s[20];
    int rem[16],A[16],W[16],K[16],ans,jry[16];
    int C[16],D[16],H[16],J[16],P[16];
    int code(char c) {
        if(c >= '4' && c <= '9') return c - '4' + 1;
        if(c == 'T') return 7;
        if(c == 'J') return 8;
        if(c == 'Q') return 9;
        if(c == 'K') return 10;
        if(c == 'A') return 11;
        if(c == '2') return 12;
        if(c == 'w') return 13;
        if(c == 'W') return 14;
    }
    bool check(int f,int t) {
        for(int i = 0 ; i <= t ; ++i) {
        memcpy(H,W,sizeof(H));
        memcpy(J,K,sizeof(K));
        if(2 * i + t - i + f * 2  + f * 4 + t * 3 > 17) break;
        int cnt = 0;
        for(int j = 1 ; j <= 14 ; ++j) {
            if(H[j] >= 2 && cnt < i) {H[j] -= 2;++cnt;}
            if(H[j] >= 2 && cnt < i) {H[j] -= 2;++cnt;}
            if(cnt == i) break;
        }
        if(cnt < i) break;
        cnt = 0;
        for(int j = 14 ; j >= 1 ; --j) {
            if(J[j] >= 2 && cnt < i) {J[j] -= 2;++cnt;}
            if(J[j] >= 2 && cnt < i) {J[j] -= 2;++cnt;}
            if(cnt == i) break;
        }
        if(cnt < i) break;
        memset(P,0,sizeof(P));
        cnt = 2 * f + t - i;
        for(int j = 14 ; j >= 1 ; --j) {
            int t = min(cnt,J[j]);
            J[j] -= t;cnt -= t;
            if(cnt == 0) break;
        }
        if(cnt) continue;
        cnt = 2 * f + t - i;
        for(int j = 1 ; j <= 14 ; ++j) {
            int t = min(cnt,H[j]);
            H[j] -= t;cnt -= t;
            if(cnt == 0) break;
        }
        if(J[14]) continue;
        for(int j = 1 ; j <= 14 ; ++j) {
            P[j] += H[j];
            P[j + 1] -= J[j];
        }
        cnt = 0;
        for(int j = 1 ; j <= 14 ; ++j) {
            cnt += P[j];
            if(cnt > 0) break;
        }
        if(cnt == 0) return true;
        }
        return false;
    }
    bool brute_jry(int dep,int four,int three,int f,int t,int q1,int q2) {
        if(four == f && t == three) return check(four,three);
        if(dep >= 12) return false;
        q1 += C[dep];q2 += D[dep];
        if(q1 > 0 || q2 > 0) return false;
        if(K[dep] >= 3) {
        K[dep] -= 3;
        if(brute_jry(dep + 1,four,three,f,t + 1,q1 - 1,q2)) return true;
        K[dep] += 3;
        }
        if(K[dep] >= 4) {
        K[dep] -= 4;
        if(brute_jry(dep + 1,four,three,f + 1,t,q1,q2 - 1)) return true;
        K[dep] += 4;
        }
        return brute_jry(dep + 1,four,three,f,t,q1,q2);
    }
    bool brute_wangyou(int dep,int four,int three) {
        if(four * 6 + three * 4 > 17) return false;
        if(dep > 12) {
        if(brute_jry(1,four,three,0,0,0,0)) return true;
        return false;
        }
        if(W[dep] >= 3) {
        W[dep] -= 3;++C[dep];
        if(brute_wangyou(dep + 1,four,three + 1)) return true;
        W[dep] += 3;--C[dep];
        }
        if(W[dep] >= 4) {
        W[dep] -= 4;++D[dep];
        if(brute_wangyou(dep + 1,four + 1,three)) return true;
        W[dep] += 4;--D[dep];
        }
        if(brute_wangyou(dep + 1,four,three)) return true;
        return false;
    }
    void dfs(int dep,int r) {
        if(!r) {
        memset(C,0,sizeof(C));
        memset(D,0,sizeof(D));
        memcpy(W,A,sizeof(A));
        memcpy(K,jry,sizeof(jry));
        if(brute_wangyou(2,0,0)) {++ans;}
        return;
        }
        if(dep > 14) return;
        for(int i = 0 ; i <= rem[dep] ; ++i) {
        if(i > r) break;
        jry[dep] = i;
        dfs(dep + 1,r - i);
        jry[dep] = 0;
        }
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        while(scanf("%s",s + 1) != EOF) {
        memset(A,0,sizeof(A));
        for(int i = 1 ; i <= 12 ; ++i) rem[i] = 4;
        rem[13] = rem[14] = 1;
        ans = 0;
        for(int i = 1 ; i <= 17 ; ++i) {
            A[code(s[i])]++;rem[code(s[i])]--;
        }
        dfs(1,17);
        out(ans);enter;
        }
    }
    
    // Copyright lzt
    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef std::pair<int, int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef std::pair<long long, long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i, j, k)  for (register int i = (int)(j); i <= (int)(k); i++)
    #define rrep(i, j, k) for (register int i = (int)(j); i >= (int)(k); i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    inline ll read() {
      ll x = 0, f = 1;
      char ch = getchar();
      while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
      }
      while (ch <= '9' && ch >= '0') {
        x = 10 * x + ch - '0';
        ch = getchar();
      }
      return x * f;
    }
    
    const int maxn = 30;
    int a[maxn], b[maxn], c[maxn], d[maxn], e[maxn];
    int dy[400], ans;
    char s[maxn];
    
    int B(int o,int p) {
      for(int i=0;i<=o;i++) {
        int x=o+p-i,y=i;
        for(int j=0;j<14;j++) d[j]=c[j];
        for(int j=13;~j;j--) {
          while(y&&d[j]>=2) d[j]-=2,--y;
          while(x&&d[j]) --d[j],--x;
        }
        if(x|y) continue;
        for(int j=0;j<14;j++) e[j]=a[j];
        x=o+p-i,y=i;
        for(int j=0;j<14;j++) {
          while(y&&e[j]>=2) e[j]-=2,--y;
          while(x&&e[j]) --e[j],--x;  
        }
        if(x|y) continue;
        x=0,y=1;
        for(int j=0;j<14;j++) {
          if(e[j]>x) y=0;
          x+=d[j]-e[j];
        }
        if(y) return 1;
      }
      return 0;
    }
    int C(int x,int o,int p,int q,int r) {
      if(x==14) return !q&&!r&&B(o,p);
      int f;
      if(c[x]>=4) {
        c[x]-=4,f=C(x+1,o,p+2,q,r+1),c[x]+=4;
        if(f) return 1;
      }
      if(c[x]>=3) {
        c[x]-=3,f=C(x+1,o+1,p,q+1,r),c[x]+=3;
        if(f) return 1;
      }
      if(a[x]>=4&&r) {
        a[x]-=4,f=C(x+1,o,p,q,r-1),a[x]+=4;
        if(f) return 1; 
      }
      if(a[x]>=3&&q) {
        a[x]-=3,f=C(x+1,o,p,q-1,r),a[x]+=3;
        if(f) return 1;
      }
      return C(x+1,o,p,q,r);
    }
    inline void dfs(int x, int s) {
      if (x == 14) {
        if (!s && C(0, 0, 0, 0, 0)) ++ans;
        return;
      }
      for (int i = 0; i <= s && i <= b[x]; i++) c[x] = i, dfs(x + 1, s - i);
    }
    
    void work() {
      dy['T'] = 6; dy['J'] = 7; dy['Q'] = 8; dy['K'] = 9;
      dy['A'] = 10; dy['2'] = 11; dy['w'] = 12; dy['W'] = 13;
      scanf("%s", s);
      rep(i, 0, 11) b[i] = 4; b[12] = b[13] = 1;
      rep(i, 0, 16) {
        int x;
        if (s[i] >= '4' && s[i] <= '9') x = s[i] - '4';
        else x = dy[s[i]];
        ++a[x]; --b[x];
      }
      dfs(0, 17);
      printf("%d
    ", ans);
    }
    
    int main() {
      #ifdef LZT
        freopen("in", "r", stdin);
      #endif
    
      work();
    
      #ifdef LZT
        Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
      #endif
    }
    

    Day 2

    T1 星际穿越

    Solution

    首先想暴力

    发现一个点到左侧一个点的最短路,一定是最多向右跑一次之后一直向左跑

    每次取能达到的最小的 (L) 继续跑

    下面想正解

    不难看出所有边能够形成一棵树,而且在点树上只向上跑,那么考虑倍增

    首先令 (f[i][j]) 表示 ([i, n]) 中的所有点向左跑 (2^j) 步最远能够到达的位置,(s[i][j]) 表示从 (i) 这个点向左跑,跑到所有 ([i - 2^j, i]) 之间的点的距离和

    都可以倍增

    (f[i][j] = f[f[i][j-1]][j-1])

    (s[i][j]=s[i][j-1]+s[f[i][j-1]][j-1]+(f[i][j-1]-f[i][j])cdot 2^{j-1})

    这个自己想一下就好了

    剩下的就是第一步向左走还是向右走的问题了

    一个小技巧:我们先向左走一步

    这样的话,第一步向左走已经统计在里面了,这一步经过的点距离都为 (1),加进答案里即可

    第一步向右走的话,肯定是因为右边有一个点的 (L) 值比较小,比当前这个位置直接向左走两步还要小

    那么我们从 (i) 的右边那个点开始和从 (L[i]) 开始没有区别

    这样就规避了先向右走的影响

    Code
    // Copyright lzt
    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef std::pair<int, int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef std::pair<long long, long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i, j, k)  for (register int i = (int)(j); i <= (int)(k); i++)
    #define rrep(i, j, k) for (register int i = (int)(j); i >= (int)(k); i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    inline ll read() {
      ll x = 0, f = 1;
      char ch = getchar();
      while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
      }
      while (ch <= '9' && ch >= '0') {
        x = 10 * x + ch - '0';
        ch = getchar();
      }
      return x * f;
    }
    
    const int maxn = 300300;
    int n;
    ll sum[maxn][22], pw[22];
    int to[maxn][22], L[maxn];
    
    ll calc(int l, int r) {
      if (L[r] <= l) return r - l;
      ll ans = r - L[r]; r = L[r]; int tot = 1;
      rrep(i, 20, 0) if (to[r][i] > l) {
        ans += sum[r][i] + tot * (r - to[r][i]);
        r = to[r][i]; tot += pw[i];
      }
      return ans + (r - l) * 1ll * (tot + 1);
    }
    
    void work() {
      L[1] = 1; pw[0] = 1; n = read();
      rep(i, 2, n) L[i] = read();
      rep(i, 1, n) pw[i] = pw[i - 1] * 2ll;
      to[n][0] = L[n]; sum[n][0] = n - L[n];
      rrep(i, n - 1, 1) {
        to[i][0] = min(to[i + 1][0], L[i]);
        sum[i][0] = i - to[i][0];
      }
      rep(j, 1, 20) rep(i, 1, n) {
        if (to[i][j - 1]) {
          to[i][j] = to[to[i][j - 1]][j - 1];
          sum[i][j] = sum[i][j - 1] + sum[to[i][j - 1]][j - 1];
          sum[i][j] += (to[i][j - 1] - to[i][j]) * pw[j - 1];
        }
      }
      int q = read();
      while (q--) {
        int l = read(), r = read(), x = read();
        ll a = calc(l, x) - calc(r + 1, x), b = r - l + 1;
        printf("%lld/%lld
    ", a / __gcd(a, b), b / __gcd(a, b));
      }
    }
    
    int main() {
      #ifdef LZT
        freopen("in", "r", stdin);
      #endif
    
      work();
    
      #ifdef LZT
        Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
      #endif
    }
    
  • 相关阅读:
    python中print打印显示颜色
    django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3
    Ubuntu下Pycharm专业版安装
    解决Windows10运行VMware Workstation出现与Device Guard不兼容导致无法创建与运行虚拟机问题
    Script file 'D:\Anaconda3\Scripts\pipscript.py' is not present.
    Python中*args和**kwargs
    解决Pycharm运行卡顿问题
    一千行 MySQL 学习笔记,值得收藏!
    Ubuntu下安装QQ微信——亲测试有效!
    Windows 10专业版中如何添加 docker
  • 原文地址:https://www.cnblogs.com/wawawa8/p/10173243.html
Copyright © 2011-2022 走看看