• 2017 Bangladesh National High School Programming Contest ( National Round, Senior Group ), NHSPC 2017 题解


    题目链接

    A. Charm Is Not Always Enough 

    模拟一下就可以了。

    #include <bits/stdc++.h>
    using namespace std;
    
    int T;
    
    int main() {
      scanf("%d", &T);
      while(T --) {
        int n, m;
        long long ans = 0;
        scanf("%d%d", &n, &m);
        while(n --) {
          int x;
          scanf("%d", &x);
          x = x % m;
          if(x == 0) continue;
          ans = ans + 1LL * (m - x);
        }
        cout << ans << endl;
      }
      return 0;
    }
    

    B. Max and Alexis Plan to Conquer the World

    打表。

    设比例为$h$,可以发现$x$天之后的数量等于$n$乘上一个关于$h$的某种前缀和。

    $h$只有$100$种,可以把每一种的前缀和都计算好,每组数据二分一下即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    double h[105][4500];
    
    void init() {
      for(int i = 1; i <= 100; i ++) {
        h[i][0] = 1.0;
        for(int t = 1; t < 4500; t ++) {
          h[i][t] = h[i][t - 1] + h[i][t - 1] * i / 100;
        }
        //printf("%lf
    ", h[i][4499]);
      }
    }
    
    int main() {
      init();
      int T ;
      scanf("%d", &T);
      int cas = 1;
      while(T -- > 0) {
        double n;
        scanf("%lf", &n);
        int r;
        scanf("%d", &r);
        double p;
        scanf("%lf", &p);
        
        
        int L = 0, R = 4499;
        int ans = 0;
        while(L <= R) {
          int mid = (L + R) / 2;
          if(n * h[r][mid] >= p) {
            ans = mid;
            R = mid - 1;
          } else {
            L = mid + 1;
          }
        }
        printf("Case %d: %d
    ", cas, ans);
        cas ++;
      }
    }
    

    C. Being Common is Too Mainstream 

    质因数分解,暴力。

    #include <bits/stdc++.h>
    using namespace std;
    
    //****************************************************************
    // Miller_Rabin 算法进行素数测试
    //速度快,而且可以判断 <2^63的数
    //****************************************************************
    const int S=20;//随机算法判定次数,S越大,判错概率越小
    
    
    //计算 (a*b)%c.   a,b都是long long的数,直接相乘可能溢出的
    //  a,b,c <2^63
    long long mult_mod(long long a,long long b,long long c)
    {
      a%=c;
      b%=c;
      long long ret=0;
      while(b)
      {
        if(b&1){ret+=a;ret%=c;}
        a<<=1;
        if(a>=c)a%=c;
        b>>=1;
      }
      return ret;
    }
    
    //计算  x^n %c
    long long pow_mod(long long x,long long n,long long mod)//x^n%c
    {
      if(n==1)return x%mod;
      x%=mod;
      long long tmp=x;
      long long ret=1;
      while(n)
      {
        if(n&1) ret=mult_mod(ret,tmp,mod);
        tmp=mult_mod(tmp,tmp,mod);
        n>>=1;
      }
      return ret;
    }
    
    //以a为基,n-1=x*2^t      a^(n-1)=1(mod n)  验证n是不是合数
    //一定是合数返回true,不一定返回false
    bool check(long long a,long long n,long long x,long long t)
    {
      long long ret=pow_mod(a,x,n);
      long long last=ret;
      for(int i=1;i<=t;i++)
      {
        ret=mult_mod(ret,ret,n);
        if(ret==1&&last!=1&&last!=n-1) return true;//合数
        last=ret;
      }
      if(ret!=1) return true;
      return false;
    }
    
    // Miller_Rabin()算法素数判定
    //是素数返回true.(可能是伪素数,但概率极小)
    //合数返回false;
    
    bool Miller_Rabin(long long n)
    {
      if(n<2)return false;
      if(n==2)return true;
      if((n&1)==0) return false;//偶数
      long long x=n-1;
      long long t=0;
      while((x&1)==0){x>>=1;t++;}
      for(int i=0;i<S;i++)
      {
        long long a=rand()%(n-1)+1;//rand()需要stdlib.h头文件
        if(check(a,n,x,t))
          return false;//合数
      }
      return true;
    }
    
    
    //************************************************
    //pollard_rho 算法进行质因数分解
    //************************************************
    long long factor[100];//质因数分解结果(刚返回时是无序的)
    int tol;//质因数的个数。数组小标从0开始
    
    long long gcd(long long a,long long b)
    {
      if(a==0)return 1;//???????
      if(a<0) return gcd(-a,b);
      while(b)
      {
        long long t=a%b;
        a=b;
        b=t;
      }
      return a;
    }
    
    long long Pollard_rho(long long x,long long c)
    {
      long long i=1,k=2;
      long long x0=rand()%x;
      long long y=x0;
      while(1)
      {
        i++;
        x0=(mult_mod(x0,x0,x)+c)%x;
        long long d=gcd(y-x0,x);
        if(d!=1&&d!=x) return d;
        if(y==x0) return x;
        if(i==k){y=x0;k+=k;}
      }
    }
    //对n进行素因子分解
    void findfac(long long n)
    {
      if(Miller_Rabin(n))//素数
      {
        factor[tol++]=n;
        return;
      }
      long long p=n;
      while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);
      findfac(p);
      findfac(n/p);
    }
    
    
    const long long mod = 1000000001LL;
    const int maxn = 1e5 + 10;
    long long a[maxn];
    vector<long long> fac[maxn];
    
    bool prime(long long x) {
      if(x == 1) return 0;
      for(long long i = 2; i * i <= x; i ++) {
        if(x % i == 0) return 0;
      }
      return 1;
    }
    
    int main() {
      srand(time(NULL));
      int n;
      scanf("%d", &n);
      for(int i = 1; i <= n; i ++) {
        scanf("%lld", &a[i]);
      }
      long long ans = 1;
      if(n == 1) {
        ans = a[1] % mod;
      } else if(n == 2) {
        long long g = gcd(a[1], a[2]);
        for(int i = 1; i <= n; i ++) {
          a[i] /= g;
          ans = ans * a[i] % mod;
        }
      } else if(n == 3) {
        long long g;
        g = gcd(a[1], gcd(a[2], a[3]));
        for(int i = 1; i <= n; i ++) {
          a[i] /= g;
        }
        g = gcd(a[1], a[2]);
        a[1] /= g;
        a[2] /= g;
        g = gcd(a[2], a[3]);
        a[2] /= g;
        a[3] /= g;
        g = gcd(a[1], a[3]);
        a[1] /= g;
        a[3] /= g;
        for(int i = 1; i <= n; i ++) {
          ans = ans * a[i] % mod;
        }
      } else if(n <= 1000) {
        for(int i = 1; i <= n; i ++) {
          if(a[i] == 1) continue;
          tol = 0;
          findfac(a[i]);
          for(int j = 0; j < tol; j ++) {
            fac[i].push_back(factor[j]);
          }
        }
        for(int i = 1; i <= n; i ++) {
          for(int j = 0; j < fac[i].size(); j ++) {
            if(a[i] % fac[i][j]) continue;
            int num = 0;
            for(int k = 1; k <= n; k ++) {
              if(a[k] % fac[i][j] == 0) num ++;
            }
            if(num < 2) continue;
            for(int k = 1; k <= n; k ++) {
              if(a[k] % fac[i][j] == 0) {
                a[k] /= fac[i][j];
              }
            }
          }
        }
        for(int i = 1; i <= n; i ++) {
          ans = ans * a[i] % mod;
        }
      } else {
        for(long long x = 2; x <= 601; x ++) {
          if(!prime(x)) continue;
          while(1) {
            int num = 0;
            for(int i = 1; i <= n; i ++) {
              if(a[i] % x == 0) num ++;
            }
            if(num < 2) break;
            for(int i = 1; i <= n; i ++) {
              if(a[i] % x == 0) a[i] /= x;
            }
          }
        }
        for(int i = 1; i <= n; i ++) {
          ans = ans * a[i] % mod;
        }
      }
      printf("%lld
    ", ans);
      return 0;
    }
    
    /*
     10
     1 2 3 4 5 6 7 8 9 10
     */
    

    D. Shaat Chara 

    对于第$i$堆石头,要使得拿走第$i$堆的若干颗石头变成必胜态,也就是要使得剩下的所有石头异或和为$0$。

    #include <bits/stdc++.h>
    using namespace std;
    
    const long long mod = 1000000007LL;
    const int maxn = 2e5 + 10;
    int T, n;
    int a[maxn];
    
    int main() {
      scanf("%d", &T);
      int cas = 1;
      while(T --) {
        scanf("%d", &n);
        int Xor = 0;
        for(int i = 1; i <= n; i ++) {
          scanf("%d", &a[i]);
          Xor = Xor ^ a[i];
        }
        int ans = 0;
        for(int i = 1; i <= n; i ++) {
          Xor = Xor ^ a[i];
          if(Xor < a[i]) ans ++;
          Xor = Xor ^ a[i];
        }
        printf("Case %d: %d
    ", cas ++, ans);
      }
      return 0;
    }
    

    E. Just One Swap 

    如果每个数字都不一样,答案就是$C_n^2$。

    否则,相同的数字交换有$1$种情况,再计算不同数字交换的方案数。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e5 + 10;
    int T;
    int a[maxn];
    
    int main() {
      scanf("%d", &T);
      while(T --) {
        memset(a, 0, sizeof a);
        int n;
        scanf("%d", &n);
        int y = n;
        while(n --) {
          int x;
          scanf("%d", &x);
          a[x] ++;
        }
        int ok = 1;
        for(int i = 1; i <= 100000; i ++) {
          if(a[i] > 1) ok = 0;
        }
        
        long long ans = 0;
        if(ok) {
          ans = 1LL * y * (y - 1) / 2;
        } else {
          ans = 1LL;
          long long sum = 0;
          for(int i = 1; i <= 100000; i ++) {
            ans = ans + sum * a[i];
            sum = sum + a[i];
          }
        }
        printf("%lld
    ", ans);
      }
      return 0;
    }
    

    F. Halum and Candies 

    贪心,二分。

    这题最直观的做法是每次将最大的$k$个数字减$1$,直到不能操作为止,但是在题目的数据规模下容易超时。

    较为容易的写法是二分答案+验证,假设二分到$x$个人,只要看$sumlimits_{i = 1}^n {min (a[i],x)}$和$x*k$的大小关系即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    int T, n, k;
    const int maxn = 1e5 + 10;
    long long a[maxn];
    
    int check(long long x) {
      long long p = 0;
      for(int i = 1; i <= n; i ++) {
        p = p + min(x, a[i]);
      }
      if(p >= x * k) return 1;
      return 0;
    }
    
    int main() {
      int cas = 1;
      scanf("%d", &T);
      while(T --) {
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; i ++) {
          scanf("%lld", &a[i]);
        }
        long long L = 0;
        long long R = 1e12;
        long long ans = 0;
        while(L <= R) {
          long long mid = (L + R) / 2;
          if(check(mid)) ans = mid, L = mid + 1;
          else R = mid - 1;
        }
        printf("Case %d: %lld
    ", cas ++, ans);
      }
      return 0;
    }
    
    /*
     3
     3 3
     1 2 3
     3 1
     1 2 3
     3 2
     3 2 4
     */
    

    G. XOR 'em all! 

    线段树。

    每个节点存储每一种$1$的个数的最小的位置,以及转换后的即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e6 + 10;
    int T, n, q;
    int a[maxn], cnt[2 * maxn];
    int s[maxn * 4][2][25];
    int p[maxn * 4], f[maxn * 4];
    int ans, B, v;
    
    int lowbit(int x) {
      return x & (-x);
    }
    
    void init() {
      for(int i = 1; i < (1 << 20); i ++) {
        cnt[i] = cnt[i - lowbit(i)] + 1;
      }
    }
    
    void pushUp(int rt) {
      for(int i = 0; i < 21; i ++) {
        s[rt][0][i] = min(s[2 * rt][p[2 * rt]][i],
                          s[2 * rt + 1][p[2 * rt + 1]][i]);
        s[rt][1][i] = min(s[2 * rt][p[2 * rt] ^ 1][i],
                          s[2 * rt + 1][p[2 * rt + 1] ^ 1][i]);
      }
      p[rt] = 0;
    }
    
    void pushDown(int rt) {
      if(f[rt] == 0) return;
      p[2 * rt] = (p[2 * rt] + f[rt]) % 2;
      f[2 * rt] = (f[2 * rt] + f[rt]) % 2;
      p[2 * rt + 1] = (p[2 * rt + 1] + f[rt]) % 2;
      f[2 * rt + 1] = (f[2 * rt + 1] + f[rt]) % 2;
      f[rt] = 0;
    }
    
    void build(int l, int r, int rt) {
      p[rt] = 0;
      f[rt] = 0;
      if(l == r) {
        for(int t = 0; t < 2; t ++) {
          for(int i = 0; i < 21; i ++) {
            s[rt][t][i] = n + 1;
          }
        }
        s[rt][0][a[l]] = l;
        s[rt][1][20 - a[l]] = l;
        return;
      }
      int mid = (l + r) / 2;
      build(l, mid, 2 * rt);
      build(mid + 1, r, 2 * rt + 1);
      pushUp(rt);
    }
    
    void update(int L, int R, int l, int r, int rt) {
      if(L <= l && r <= R) {
        p[rt] = (p[rt] + 1) % 2;
        f[rt] = (f[rt] + 1) % 2;
        return;
      }
      pushDown(rt);
      int mid = (l + r) / 2;
      if(L <= mid) update(L, R, l, mid, 2 * rt);
      if(R > mid) update(L, R, mid + 1, r, 2 * rt + 1);
      pushUp(rt);
    }
    
    void query(int L, int R, int l, int r, int rt) {
      if(L <= l && r <= R) {
        for(int i = 0; i < 21; i ++) {
          if(s[rt][p[rt]][i] > n) continue;
          if(abs(i - v) < B) {
            B = abs(i - v);
            ans = s[rt][p[rt]][i];
          } else if(abs(i - v) == B) {
            ans = min(ans, s[rt][p[rt]][i]);
          }
        }
        return;
      }
      pushDown(rt);
      int mid = (l + r) / 2;
      if(L <= mid) query(L, R, l, mid, 2 * rt);
      if(R > mid) query(L, R, mid + 1, r, 2 * rt + 1);
      pushUp(rt);
    }
    
    int main() {
      init();
      scanf("%d", &T);
      int cas = 1;
      while(T --) {
        printf("Case %d:
    ", cas ++);
        scanf("%d%d", &n, &q);
        for(int i = 1; i <= n; i ++) {
          scanf("%d", &a[i]);
          a[i] = cnt[a[i]];
        }
        build(1, n, 1);
        while(q --) {
          int op, l, r;
          scanf("%d%d%d", &op, &l, &r);
          if(op == 1) {
            scanf("%d", &v);
            v = cnt[v];
            B = 100;
            ans = n + 1;
            query(l, r, 1, n, 1);
            printf("%d
    ", ans);
          } else {
            update(l, r, 1, n, 1);
          }
        }
      }
      return 0;
    }
    
    /*
     1
     10 9
     47810 337106 289217 728190 763968 210307 934334 929186 401808 365768
     2 8 10
     1 2 10 611293
     2 2 4
     1 1 8 422298
     2 6 8
     2 2 10
     1 5 6 180197
     2 7 8
     1 4 8 712158
     */
    

    H. Simple Path 

    树形$dp$。

    注意点:这题数据有问题,题面上说每条边都是从$u$到$v$的,但事实上不是。

    #include <bits/stdc++.h>
    using namespace std;
    
    const long long mod = 1000000007LL;
    const int maxn = 4e5 + 10;
    int T;
    int h[maxn];
    int v[maxn];
    long long w[maxn];
    int nx[maxn];
    int n;
    int sz[maxn];
    long long ans;
    int cnt;
    int f[maxn];
    
    void add(int a, int b, long long c) {
      v[cnt] = b;
      w[cnt] = c;
      nx[cnt] = h[a];
      h[a] = cnt ++;
    }
    
    void SZ(int x) {
      sz[x] = 1;
      f[x] = 1;
      for(int i = h[x]; i != -1; i = nx[i]) {
        if(!f[v[i]]) {
          SZ(v[i]);
          sz[x] += sz[v[i]];
        }
      }
    }
    
    void DP(int x, long long sum, int dep) {
      f[x] = 1;
      for(int i = h[x]; i != -1; i = nx[i]) {
        if(f[v[i]]) continue;
       // printf(" %d -> %d 
    ", x, v[i]);
        long long A = 1LL * sz[v[i]] * sum % mod;
        long long B = 1LL * sz[v[i]] * sz[v[i]] % mod;
        B = 1LL * B * dep % mod;
        long long C = (A - B + mod) % mod;
        C = 1LL * C * w[i] % mod;
       // cout << x << " debug " << C << endl;
        ans = (ans + C) % mod;
        long long G = 1LL * sz[v[i]];
        G = (sum + G) % mod;
        DP(v[i], G, dep + 1);
      }
      
      //printf("debug  %d %lld
    ", x, dp[x]);
    }
    
    int main() {
      scanf("%d", &T);
      int cas = 1;
      while(T --) {
        scanf("%d", &n);
        cnt = 0;
        for(int i = 1; i <= n; i ++) {
          h[i] = -1;
          sz[i] = 0;
          f[i] = 0;
        }
        for(int i = 1; i < n; i ++) {
          int a, b;
          long long c;
          scanf("%d%d%lld", &a, &b, &c);
          add(a, b, c);
          add(b, a, c);
        }
        SZ(1);
        for(int i = 1; i <= n; i ++) {
          if(sz[i] <= 0) while(1) {}
        }
        for(int i = 1; i <= n; i ++) {
          f[i] = 0;
        }
        ans = 0;
        DP(1, sz[1], 1);
        printf("Case %d: %lld
    ", cas ++, ans);
      }
      return 0;
    }
    
    /*
     2
     7
     1 2 3
     1 3 2
     2 4 1
     2 5 4
     3 6 6
     3 7 8
     
     6
     1 2 3
     1 3 2
     1 4 4
     3 5 7
     3 6 1
     */
    
  • 相关阅读:
    soapUI-DataSource
    Linux安装rpm包时报错Header V3 DSA/SHA1 Signature, key ID 1d1e034b: NOKEY解决办法
    Linux命令之rpm安装命令
    soapUi下载
    Red Hat Linux相关产品iso镜像下载
    RedHat Linux文本模式下乱码解决方法
    telnet到RedHat Linux失败--解决办法
    java list去重
    Java Mybatis 框架入门教程
    【阿里天池云-龙珠计划】薄书的机器学习笔记——K近邻(k-nearest neighbors)初探Task02
  • 原文地址:https://www.cnblogs.com/zufezzt/p/8398913.html
Copyright © 2020-2023  润新知