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\n", 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\n", 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\n", 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\n", 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\n", ans);
  }
  return 0;
}

 

F. Halum and Candies 

贪心,二分。

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

较为容易的写法是二分答案+验证,假设二分到$x$个人,只要看$\sum\limits_{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\n", 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:\n", 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\n", 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 \n", 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\n", 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\n", 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
 */

 

posted @ 2018-02-01 14:36  Fighting_Heart  阅读(592)  评论(0编辑  收藏  举报