ACM-ICPC 2017 Asia Xi'an

Solved A B C D E F G H I J K
7/11 O O Ø O O ? O O
  • O for passing during the contest
  • Ø for passing after the contest
  • ! for attempted but failed
  • · for having not attempted yet

A - XOR

计蒜客 - A1607

solved by hyd+dyp

题意:长度为\(n\)的数组\(a\),\(q\)次询问,每次询问给出 \(l\),\(r\),问从\(a[l],a[l+1],\cdots a[r]\)中取出若干个数字,使得其异或和跟一个固定数字$ k$ 进行或运算所得结果的最大值。\(n\le 10000,q\le 100000,k\le100000, a[i]\le 1e8\)

分析:区间内选出若干个数字的异或和,我们先不考虑跟固定数值 k 的运算,如果要从中选出最大的异或和,很容易想到前缀线性基求解(或者是线段树合并线性基),在线性基里面按位查找即可得到最大值。

贪心的维护序列的前缀线性基(上三角形态),对于每个线性基,将出现位置靠右的数字尽可能地放在高位,也就是说在插入新数字的时候,要同时记录对应位置上数字的出现位置,并且在找到可以插入的位置的时候,如果新数字比位置上原来的数字更靠右,就将该位置上原来的数字向低位推。

在求最大值时候,从高位向低位遍历,如果该位上的数字出现在询问中区间左端点的右侧且可以使答案变得更大,就异或到答案里面。

到这里可以顺便看看这个题:链接, 你或许可以直接把他A掉。

然后再考虑跟固定 k 的操作,我们知道 k 的二进制表示中,那些为 1 的位是没有用的,因为最终求出来的异或和要跟 k 进行或运算,所以除去这些位,我们应当贪心的去考虑高位(这与普通线性基插入时优先考虑高位是一样的),并且忽略掉 k 的二进制中为 1 的那些位。

为什么要这么考虑呢?如果在插入某个数到线性基之前,不考虑 k 的因素而直接贪心的插入,那么线性基中为了可以异或出更大的值,某个数字可能会以较高的优先级插入到某个高位,而这一位可能对于 k 来说已经没有任何用了。

举个例子,如果线性基中有这么两个数字:1001,0111,k 为 1000。那么直接构建线性基从中得到最大值为 1110,跟 k 或得到 1110,而答案应该是:1111

  1. const int N = 100000 + 5;
  2. int T, n, q, k;
  3. int a[N];
  4. struct Base{
  5. int a[31];
  6. int pos[31];
  7. int cnt;
  8. Base(){
  9. cnt =0;memset(a,0,sizeof a);
  10. memset(pos, 0, sizeof pos);
  11. }
  12. void clear(){
  13. cnt = 0; memset(a, 0, sizeof a);
  14. memset(pos, 0, sizeof pos);
  15. }
  16. bool ins(int x, int id){
  17. for(int i=29;i>=0;--i){
  18. if(x >> i & 1){
  19. if(a[i]){ //如果原来的位置已经有数字了
  20. if(id > pos[i]){ //而且当前数字的 id 更大,为了在更高的位保留更靠右出现的数字,下面要做两个数字的交换操作
  21. swap(pos[i], id);
  22. swap(x, a[i]);
  23. }
  24. x ^= a[i]; //线性基插入常规操作,保持线性无关
  25. }
  26. else {
  27. cnt ++;
  28. a[i] = x;
  29. pos[i] = id;
  30. return 1;
  31. }
  32. }
  33. }
  34. return 0;
  35. }
  36. }base[N];
  37. int main(){
  38. int T;scanf("%d",&T);
  39. while(T--){
  40. scanf("%d%d%d",&n,&q,&k);
  41. for(int i=1;i<=n;i++){
  42. scanf("%d",&a[i]);
  43. a[i] &= ~k; // k取反后 与运算,去除 k 为 1 的那些位
  44. base[i] = base[i-1];
  45. base[i].ins(a[i], i);
  46. }
  47. while(q--){
  48. int l, r;
  49. scanf("%d%d", &l, &r);
  50. int res = 0;
  51. for(int i=29;i>=0;i--){
  52. //如果该位上面有数字,并且它的出现位置大于等于 l
  53. if(base[r].a[i] && base[r].pos[i] >= l){
  54. //异或后可以使得答案更大,则更新
  55. //这里的原因是因为我们维护的是上三角状态,并没有化为最简,可以将第一个样例带入测试
  56. if((res ^ base[r].a[i]) > res){
  57. res ^= base[r].a[i];
  58. }
  59. }
  60. }
  61. res |= k;
  62. printf("%d\n", res);
  63. }
  64. }
  65. return 0;
  66. }

B - Lovers

计蒜客 - A1608

solved by lwq

从前到后考虑即可,因为它的右边界是单调递增的

  1. const int N = 200000 + 5;
  2. int T, n, k;
  3. int a[N], b[N];
  4. int main(){
  5. scanf("%d", &T);
  6. while(T--){
  7. scanf("%d%d", &n, &k);
  8. for(int i=1;i<=n;i++)scanf("%d", &a[i]);
  9. for(int i=1;i<=n;i++)scanf("%d", &b[i]);
  10. sort(a + 1, a + 1 + n);
  11. sort(b + 1, b + 1 + n);
  12. int res = 0, l = 1;
  13. for(int i=n;i>=1;i--){
  14. while(l <= n && a[l] + b[i] < k) l++;
  15. if(l <= n) l++, res++;
  16. }
  17. printf("%d\n", res);
  18. }
  19. return 0;
  20. }

E - Naomi with Graph

计蒜客 - A1611

  • 最小割

考虑dist数组,它最终必然满足以下两个条件:

  1. \(\forall i \in [1, n], dist[i] \ge 0, dist[1] = 0\)
  2. 若原图存在\((u,v)\)这条边,则\(|dist[u]-dist[v]| \le 1\)

对于每个点 \(x\),分成 \(n\) 个点,连成一条链,头接源点,尾接汇点,从源点到汇点的第 i 个点表示 \(dist[x]\) 的值,那么最终在最小割中,割掉的边就代表着它的最短路长度。

对于原图中存在的边,由于他们存在着\(|dist[u]-dist[v]|\le 1\) 的限制,要对于 \(u\) 和 \(v\) 的两组边进行连边

设函数\(f(x,d) = (d-A[x])^2\)

如果最终答案中\(dist[v] = 2\) 那么我们把红叉的边割掉,这就导致了对于 u 这条链,我们只能选择蓝框所选的三条边中的一个,满足了\(|dist[u]-dist[v]|\le 1\)。两组点之间连流量\(\inf\) 是为了防止被割掉,这样的边割掉是没有意义的。

  1. const int N = 10000 + 5;
  2. const int M = 1000010;
  3. vector<int> g[44];
  4. int dist[44], val[44], id[44][44];
  5. int head[N], ver[M], edge[M], nxt[M], d[N];
  6. int n, m, s, t, tot, maxflow;
  7. queue<int> q;
  8. void add(int x, int y, int z){
  9. ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
  10. ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
  11. }
  12. bool bfs(bool type){
  13. memset(d, 0, sizeof d);
  14. while(q.size())q.pop();
  15. q.push(s);d[s] = 1;
  16. while(q.size()){
  17. int x = q.front();q.pop();
  18. for(int i=head[x];i;i=nxt[i]){
  19. if(edge[i] && !d[ver[i]]){
  20. q.push(ver[i]);
  21. d[ver[i]] = d[x] + 1;
  22. if(ver[i] == t) return 1;
  23. }
  24. }
  25. }
  26. return 0;
  27. }
  28. int dinic(int x, int flow){
  29. if(x == t) return flow;
  30. int rest = flow, k;
  31. for(int i=head[x];i && rest; i=nxt[i]){
  32. if(edge[i] && d[ver[i]] == d[x] + 1){
  33. k = dinic(ver[i], min(rest, edge[i]));
  34. if(!k) d[ver[i]] = 0;
  35. edge[i] -= k;
  36. edge[i ^ 1] += k;
  37. rest -= k;
  38. }
  39. }
  40. return flow - rest;
  41. }
  42. void bfs(){
  43. memset(dist, 0, sizeof dist);
  44. dist[1] = 0;
  45. queue<int> q;
  46. q.push(1);
  47. while(q.size()){
  48. int x = q.front();q.pop();
  49. for(int i=0;i<g[x].size();i++){
  50. int y = g[x][i];
  51. if(dist[y])continue;
  52. dist[y] = dist[x] + 1;
  53. q.push(y);
  54. }
  55. }
  56. }
  57. void add(int x){
  58. for(auto y : g[x]){
  59. for(int d = 0; d + 1 <n; d++){
  60. int u = id[y][d+1], v = id[x][d];
  61. add(u, v, inf);
  62. }
  63. }
  64. }
  65. int main(){
  66. while(~scanf("%d%d", &n,&m)){
  67. for(int i=1;i<=n;i++) g[i].clear();
  68. memset(head, 0, sizeof head);
  69. maxflow = 0;
  70. tot = 1;
  71. int cnt = 0;
  72. for(int i=1;i<=m;i++){
  73. int x,y;scanf("%d%d", &x,&y);
  74. g[x].push_back(y);
  75. g[y].push_back(x);
  76. }
  77. bfs();
  78. for(int i=1;i<=n;i++)scanf("%d", &val[i]);
  79. for(int i=1;i<=n;i++)for(int j=0;j<n;j++) id[i][j] = ++cnt;
  80. s = 0, t = cnt + 1;
  81. for(int i=1;i<=n;i++){
  82. add(i);
  83. for(int j=0;j<=n;j++){
  84. int u, v = id[i][j], z = (val[i]-j)*(val[i]-j);
  85. u = j == 0 ? s : id[i][j-1];
  86. if(j == n) v = t;
  87. if(j > dist[i]) z = inf;
  88. add(u, v, z);
  89. }
  90. }
  91. int flow = 0;
  92. while(bfs(1)) while(flow = dinic(s, inf)) maxflow += flow;
  93. printf("%d\n", maxflow);
  94. }
  95. return 0;
  96. }

F - God of Gamblers

计蒜客 - A1612

solved by us

并不会证明,但是感觉上理解就是他们赢的概率都是一样的,并且赌金很公平(都是 k)。所以比的就是两个人继续进行游戏的能力,所以就是 \(\frac{n}{n+m}\)

  1. double n, m;
  2. int main(){
  3. while(~scanf("%lf%lf",&n,&m)){
  4. printf("%.5f\n",(n/(n+m)));
  5. }
  6. return 0;
  7. }

G - Sum of xor sum

计蒜客 - A1613

solved by hyd

先求出序列的异或前缀和\(s[i] = a[1] ~xor~a[2]~\cdots a[i]\),可以发现对于每个询问\([l,r]\), 其实就是求\(s[l-1], s[l],\cdots s[r]\)中每对数的异或,然后取和。

观察到\(a[i]\le 1000000\), 最多20位,所以分 20 位进行考虑,现在区间内变成了两种数字,0,1.

这些数字进行异或,只有 0 与 1 异或可以对答案产生贡献,所以我们只需要维护区间内 1 的个数就好了。

对于二进制的第 k 位,该区间内有\(num1\) 个1,\(num0\) 个0,那么该位对答案的贡献为 \((1<<k)\times num0\times num1\)

(对于这种题,可以拍一些小数据(区间小,数字正常),基本可以防止wa掉)

  1. const int N = 100000 + 5;
  2. const int mod = 1e9 + 7;
  3. int T, n, m, a[N];
  4. int bit[20][N];
  5. ll get(int *s, int l, int r){
  6. ll num1 = s[r] - s[l-1];
  7. ll num0 = (r - l + 1) - num1;
  8. return num0 * num1 % mod;
  9. }
  10. int main(){
  11. scanf("%d",&T);
  12. while(T--){
  13. scanf("%d%d", &n,&m);
  14. for(int i=1;i<=n;i++){
  15. scanf("%d", &a[i]);
  16. for(int j = 0;j < 20;j ++){
  17. bit[j][i] = a[i] >> j & 1;
  18. }
  19. }
  20. for(int j=0;j<20;j++){
  21. for(int i=1;i<=n;i++){
  22. bit[j][i] = bit[j][i-1] ^ bit[j][i];
  23. }
  24. }
  25. for(int j=0;j<20;j++){
  26. for(int i=1;i<=n;i++){
  27. bit[j][i] += bit[j][i-1];
  28. }
  29. }
  30. while(m--){
  31. int l, r;
  32. scanf("%d%d",&l,&r);
  33. l--;
  34. ll res = 0;
  35. for(int j=0;j<20;j++){
  36. res = (res + (1ll<<j) * get(bit[j], l, r)%mod)%mod;
  37. }
  38. printf("%lld\n", res);
  39. }
  40. }
  41. return 0;
  42. }

H - Arrangement for Contests

计蒜客 - A1614

这个题计蒜客上面的数据有问题,UVALive又挂了,所以代码没有地方测试,思路就是贪心,从前到后只要取,取完之后在该区间内把它减掉即可。线段树维护一下即可

J - LOL

计蒜客 - A1616

solved by hyd+dyp

状压DP

\(d[i][j]\) 表示前 i 个英雄,5个人选或没选的状态是为 j 时的方案数

然后暴力转移即可,跑的飞快(然后N^4暴力枚举算法也能过 dyptql)

复杂度:\(O(10*500*2^5)\)

  1. const int N = 100 + 5;
  2. const int mod = 1e9 + 7;
  3. char S[5][N];
  4. ll d[N][1<<5];
  5. ll A(ll n, ll m){
  6. ll res = 1;
  7. for(int i=0;i<m;i++)res = res * (n-i) % mod;
  8. return res;
  9. }
  10. ll C(ll n, ll m){
  11. ll res = 1;
  12. for(int i=0;i<m;i++) res = res * (n - i) ;
  13. for(int i=1;i<=m;i++) res = res / i ;
  14. return res % mod;
  15. }
  16. int main(){
  17. while(~scanf("%s", S[0]+1)){
  18. for(int i=1;i<5;i++)scanf("%s", S[i]+1);
  19. memset(d, 0, sizeof d);
  20. d[0][0] = 1;
  21. for(int i = 1;i <= 100; i++){
  22. for(int j = 0; j < (1<<5); j++){
  23. //前缀和直接转移
  24. d[i][j] = (d[i][j] + d[i-1][j]) % mod;
  25. for(int k = 0; k < 5; k++){
  26. if(j >> k & 1)continue;//第 k 个人已经选过
  27. if(S[k][i] == '1'){//可以添加新人的转移
  28. d[i][j|(1<<k)] = (d[i][j|(1<<k)] + d[i-1][j]) % mod;
  29. }
  30. }
  31. }
  32. }
  33. //为什么Ban掉的英雄不是排列而是组合?我也不太清楚,组合的话就可以过样例
  34. ll res = d[100][(1<<5)-1] * C(95, 5) % mod * A(90, 5) % mod *C(85, 5) % mod;
  35. printf("%lld\n", res);
  36. }
  37. return 0;
  38. }

K - LOVER II

计蒜客 - A1617

solved by lwq

先给 a 从小到大排序,要让所有女生都有人与之配对,那么在最终的选择序列中,最少需要有 1 个男生与 \(a[1]\) 的和大于等于 k,有 \(2\) 个男生 与 \(a[2]\) 的 和大于等于 k,以此类推。所以初始化一个长度为 n 的数组,值为 \(-1,-2,-3,\cdots -n\)

然后对于 b 数组的每个位置 \(l\), 找到最小的 \(r\), 使得\([l,r]\) 之间的男生可以满足上面的条件,可以发现 \(r\) 随 \(l\) 递增而递增,所以每次可以用\(O(\log n)\)的时间插入一个元素,总共花费\(O(n\log n)\) 的时间预处理出来 r 数组,然后\(O(1)\) 回答询问即可

  1. const int N = 200000 + 5;
  2. int T, n, m, k, q, l, r;
  3. int a[N], b[N], R[N];
  4. struct SegTree{
  5. int l, r;
  6. int mi, lazy;
  7. }t[N<<2];
  8. void build(int p, int l, int r){
  9. t[p].l = l, t[p].r = r;
  10. t[p].lazy = 0;
  11. if(l == r){
  12. t[p].mi = -l;
  13. return;
  14. }
  15. int mid = l + r >> 1;
  16. build(p*2, l, mid);
  17. build(p*2+1, mid+1, r);
  18. t[p].mi = min(t[p*2].mi, t[p*2+1].mi);
  19. }
  20. void pushdown(int p){
  21. if(t[p].lazy){
  22. t[p*2].mi += t[p].lazy;
  23. t[p*2+1].mi += t[p].lazy;
  24. t[p*2].lazy += t[p].lazy;
  25. t[p*2+1].lazy += t[p].lazy;
  26. t[p].lazy = 0;
  27. }
  28. }
  29. void change(int p, int l, int r, int val){
  30. if(t[p].l >= l && t[p].r <= r){
  31. t[p].mi += val;
  32. t[p].lazy += val;
  33. return;
  34. }
  35. pushdown(p);
  36. int mid = t[p].l + t[p].r >> 1;
  37. if(mid >= l) change(p*2, l, r, val);
  38. if(mid < r) change(p*2+1, l, r, val);
  39. t[p].mi = min(t[p*2].mi, t[p*2+1].mi);
  40. }
  41. int main(){
  42. scanf("%d", &T);
  43. while(T--){
  44. scanf("%d%d%d", &n,&m, &k);
  45. for(int i=1;i<=n;i++)scanf("%d", &a[i]);
  46. for(int i=1;i<=m;i++)scanf("%d", &b[i]);
  47. sort(a + 1, a + 1 + n);
  48. build(1, 1, n);
  49. for(int i=1;i<=m;i++){
  50. R[i] = R[i-1];
  51. while(t[1].mi < 0){
  52. R[i] ++;
  53. if(R[i] > m) break;
  54. int pos = lower_bound(a + 1, a + 1 + n, k - b[R[i]]) - a;
  55. if(pos <= n){
  56. change(1, pos, n, 1);
  57. }
  58. }
  59. int pos = lower_bound(a + 1, a + 1 + n, k - b[i]) - a;
  60. if(pos <= n);
  61. change(1, pos, n, -1);
  62. }
  63. scanf("%d", &q);
  64. while(q--){
  65. scanf("%d%d", &l, &r);
  66. if(R[l] > m || r < R[l]){
  67. puts("0");
  68. }else{
  69. puts("1");
  70. }
  71. }
  72. }
  73. return 0;
  74. }

ACM-ICPC 2017 Asia Xi'an的更多相关文章

  1. 计蒜客 A1607 UVALive 8512 [ACM-ICPC 2017 Asia Xi'an]XOR

    ICPC官网题面假的,要下载PDF,点了提交还找不到结果在哪看(我没找到),用VJ交还直接return 0;也能AC 计蒜客题面 这个好 Time limit 3000 ms OS Linux 题目来 ...

  2. ACM ICPC 2017 Warmup Contest 9 I

    I. Older Brother Your older brother is an amateur mathematician with lots of experience. However, hi ...

  3. ACM ICPC 2017 Warmup Contest 9 L

    L. Sticky Situation While on summer camp, you are playing a game of hide-and-seek in the forest. You ...

  4. ACM-ICPC 2017 Asia Xi'an A XOR (线性基+线段树思想)

    题目链接 题意;给个数组,每次询问一个区间你可以挑任意个数的数字异或和 然后在或上k的最大值 题解:线性基不知道的先看这个,一个线性基可以log的求最大值把对应去区间的线性基求出来然后用线段树维护线性 ...

  5. ACM-ICPC 2017 Asia Xi'an J LOL 【暴力 && 排列组合】

    任意门:https://nanti.jisuanke.com/t/20750 J - LOL 5 friends play LOL together . Every one should BAN on ...

  6. ACM ICPC 2017 Warmup Contest 1 D

    Daydreaming Stockbroker Gina Reed, the famous stockbroker, is having a slow day at work, and between ...

  7. 2017 ACM/ICPC Asia Regional Shenyang Online spfa+最长路

    transaction transaction transaction Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 132768/1 ...

  8. hdu 5016 点分治(2014 ACM/ICPC Asia Regional Xi'an Online)

    Mart Master II Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)T ...

  9. 2017 ACM ICPC Asia Regional - Daejeon

    2017 ACM ICPC Asia Regional - Daejeon Problem A Broadcast Stations 题目描述:给出一棵树,每一个点有一个辐射距离\(p_i\)(待确定 ...

随机推荐

  1. 第6章 未来的函数:生成器和promise

    目录 1. 生成器函数 1.1 定义生成器函数 1.2 迭代器对象 1.3 对迭代器进行迭代 1.4 把执行权交给下一个生成器 2. 使用生成器 2.1 用生成器生成ID 2.2 用迭代器遍历DOM树 ...

  2. html 垂直并列显示

    笔者在制作登陆网页的时候,发现让input居中十分困难,笔者在网上搜了好久都没有结果,所以就想出了一个硬核的纯html的解决方法 直接上代码: <div style="text-ali ...

  3. springboot集成轻量级权限认证框架sa-token

    sa-token是什么? sa-token是一个JavaWeb轻量级权限认证框架,主要解决项目中登录认证.权限认证.Session会话等一系列由此衍生的权限相关业务.相比于其他安全性框架较容易上手. ...

  4. python模块详解 | selenium(持续更新中)

    目录: 关于selenium Selenium 安装Selenium 安装浏览器驱动 配置环境变量 selenium方法详解 定位元素 元素操作 浏览器操作 鼠标事件 浏览器事件 设置元素等待 多表单 ...

  5. 【Samba】共享服务器的搭建和相关权限设置

    1.查看防护墙 [root@zhang~ ]# /etc/init.d/iptables status   iptables:Firewall is not running.   如果没有关闭的话将他 ...

  6. 使用yaml配置文件管理资源

    [root@k8s-master ~]# vim nginx-deployment.yaml apiVersion: apps/v1beta2 kind: Deployment metadata: n ...

  7. 利用sql_tuning_Advisor调优sql

    1.赋权给调优用户 grant ADVISOR to xxxxxx; 2.创建调优任务 使用sql_text创建 DECLARE my_task_name VARCHAR2 (30); my_sqlt ...

  8. 分布式 ID 生成算法 — SnowFlake

    一.概述 分布式 ID 生成算法的有很多种,Twitter 的 SnowFlake 就是其中经典的一种. SnowFlake 算法生成 ID 的结果是一个 64bit 大小的整数,它的结构如下图: 1 ...

  9. oracle 12C单实例打PSU

    前提: oracle不管打什么样的补丁,readme都是很好的参考资料. Oracle每季度都会更新一个最新的PSU,现在12.1.0.2.0的最新的PSU是Patch 26925311. 由于今天白 ...

  10. SDNU_ACM_ICPC_2021_Winter_Practice_4th [个人赛]

    传送门 D - Odd Divisor 题意: 给你一个n,问你n是否至少有一个奇数因子(这里题意没说清,没说是不是只有一个还是可以有多个!AC以后才发现是不止一个 思路: 如果这个数没有奇数因子,那 ...