【题解】 2015 ACM/ICPC Asia Regional Changchun Online (5+2)
来源:互联网 发布:轻松工程测量系统算法 编辑:程序博客网 时间:2024/05/17 23:13
第一次网络赛 怎么说 出题数还可以 可惜没靠自己打入(巨巨学长们一举冲入前百
最后那俩没能出的一个后缀树组/字符串最小表示法+KMP 另一个lucas定理+中国剩余定理。。。(只是赛后了解到了 以后要把这几个算法补上
其他题用的都是简单算法和数据结构 不过还是卡了两处
【1001】 Alisha's Party(优先队列模拟)
Alisha’s Party
Time Limit: 3000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1066 Accepted Submission(s): 294
Each time when Alisha opens the door, she can decide to let
If there are two friends who bring gifts of the same value, then the one who comes first should enter first. Given a query
In each test case, the first line contains three numbers
The
Each of the following
The last line of each test case will contain
Note: there will be at most two test cases containing
15 2 3Sorey 3Rose 3Maltran 3Lailah 5Mikleo 61 14 21 2 3
Sorey Lailah Rose
优先队列模拟 阿丽莎过生日 邀请了k个朋友去做客 但是她卧室太小了 所以先让他们到客厅 每个朋友都带着价值v的礼物来的 阿丽莎会开卧室门m次 当第t个人来了后 她会带p个人进卧室把礼物放下 礼物价值大的先进 相同价值的 先来的先进 最后一次开门后如果还有没放下礼物的按照同样规定进卧室放礼物
没理解好 当时在纠结按m顺序来开门还是按t来开 后来WA了发 排了下序T了发。。。又改回去改了改WA。。。改回来 T。。。然后又T。。。然后不敢交了
最后楠神想起上次就是被杭电服务器给坑了 再交次吧。。。果然……2000ms变成1679@#¥#@# 糊弄鬼啊。。同样代码……哎 早起该洗脸
正确做法就是按t排个序 然后优先队列模拟开门放礼物 最后按询问输出第a次进入的人的名字即可
代码如下:
#include <bits/stdc++.h>using namespace std;typedef struct Friend{ char name[233]; int v,id; bool operator <(const struct Friend a)const//优先队列排序 逆序排 越小越靠后 { return v == a.v? id > a.id: v < a.v;//礼物价值相同的 id大的靠后 不同的 价值小的靠后 }}Friend;typedef struct Enter{ int t,p; bool operator < (const struct Enter a)const//按t排序开门 { return t == a.t? p > a.p: t < a.t; }}Enter;Enter en[155555];//开门Friend f[155555];//朋友姓名和礼物价值Friend to[155555];//第i次进卧室的朋友int ques[111];//询问int main(){ int t,k,m,q,i,j,mx,cnt,sum; scanf("%d",&t); while(t--) { priority_queue <Friend> qq; scanf("%d %d %d",&k,&m,&q); for(i = 0; i < k; ++i) { scanf("%s %d",f[i].name,&f[i].v); f[i].id = i; } for(i = 0; i < m; ++i) scanf("%d %d",&en[i].t,&en[i].p); en[m].t = k;//最后还有未进的按规则进入 所以加一次开门 en[m].p = k; sort(en,en+m+1); mx = 0; for(i = 0; i < q; ++i) { scanf("%d",&ques[i]); mx = max(mx,ques[i]);//找出最大询问 缩短时间 } cnt = 0; sum = 1; for(i = 0; i <= m && sum <= mx; ++i) { while(cnt < en[i].t)//未达到开门需要人数 朋友入队(进客厅 { qq.push(f[cnt++]); } while(!qq.empty() && en[i].p-- && sum <= mx)//p个人进卧室 队列空了/足够询问 停止进入 { to[sum++] = qq.top(); qq.pop(); } } for(i = 0; i < q; ++i) { if(i) putchar(' '); printf("%s",to[ques[i]].name); } puts(""); } return 0;}
【1002】 Ponds(拓扑+BFS)
Ponds
Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 754 Accepted Submission(s): 250
Now Betty wants to remove some ponds because she does not have enough money. But each time when she removes a pond, she can only remove the ponds which are connected with less than two ponds, or the pond will explode.
Note that Betty should keep removing ponds until no more ponds can be removed. After that, please help her calculate the sum of the value for each connected component consisting of a odd number of ponds
For each test case, the first line contains two number separated by a blank. One is the number
The next line contains
Each of the last
17 71 2 3 4 5 6 71 41 54 52 32 63 62 7
21
有环无向图 保证输入无重边 每个点具有价值 要求把所有度为0或1的点及其所连边去掉 变成一个个子图 求点为奇数的子图们的总价值(点值和)
拓扑去点 然后按要求找出去点后符合条件的图 把价值加起来即可 注意总价值会超int
代码如下:
#include <bits/stdc++.h>#define ll long longusing namespace std;typedef struct Edge{ int v,next;}Edge;Edge eg[233333];//邻接表int head[10010],val[10010],in[10010],tp,n;//head表头 val点值 in点度bool vis[10010];//访问标记void Add(int u,int v)//加点{ eg[tp].v = v; eg[tp].next = head[u]; head[u] = tp++;}void Topo()//拓扑去度<=1的点{ int i,u,v; queue <int> q; for(i = 1; i <= n; ++i)//先把度<=1的点入队并标记 { if(in[i] <= 1) { q.push(i); vis[i] = 1; } } while(!q.empty()) { u = q.front(); q.pop(); for(i = head[u]; i != -1; i = eg[i].next)//遍历减度入队 { v = eg[i].v; if(vis[v]) continue; in[v]--; if(in[v] <= 1) { q.push(v); vis[v] = 1; } } }}ll Search(int u)//遍历u所在子图 返回该图价值{ int v,i,cnt = 1;//初始点数1(u) ll ans = val[u];//初始价值u点价值 queue <int> q; q.push(u); vis[u] = 1; while(!q.empty()) { u = q.front(); q.pop(); for(i = head[u]; i != -1; i = eg[i].next) { v = eg[i].v; if(vis[v]) continue; cnt++; q.push(v); ans += val[v]; vis[v] = 1; } } if(cnt%2) return ans;//奇数点价值为点值和 偶数点价值0 else return 0;}int main(){ int t,m,i,u,v; ll sum; scanf("%d",&t); while(t--) { scanf("%d %d",&n,&m); memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); memset(in,0,sizeof(in)); sum = tp = 0; for(i = 1; i <= n; ++i) scanf("%d",&val[i]); for(i = 0; i < m; ++i) { scanf("%d %d",&u,&v); Add(u,v); Add(v,u); in[u]++; in[v]++; } Topo(); for(i = 1; i <= n; ++i) { if(vis[i]) continue; sum += Search(i); } printf("%I64d\n",sum); } return 0;}
【1005】 Travel(并查集)
Travel
Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 495 Accepted Submission(s): 205
For each test case, the first line consists of three integers
Each of the following
Then
Note that
15 5 32 3 63341 5 157243 5 57054 3 123821 3 2172660001000013000
2612
n个城市 m个旅游方案 q次查询
每个方案u v w表示在u跟v间旅游需要w时间
旅游多对城市花费时间为这些花费中的最大值 每次询问花x时间能观赏多少对城市
每个城市可以当做图中一个点 两点间花费可看做一条带权无向边 以wi时间游览ui vi 可同时游览u,v间时间花费不大于wi的城市
也就是说 需要不大于wi时间的与u成对的城市 和与v成对的城市 可互相访问
演化成两个集合间城市两两访问 把输入按w排序 然后走并查集就行 同时开个数组 记录与每个点可访问的点数(即该点所在集合包含的点数)
每加一条边时 新加入的可访问城市对数即为两集合点数相乘再乘2(无向边)
代码如下:
#include <bits/stdc++.h>using namespace std;typedef struct Edge{ int u,v,w; bool operator < (const struct Edge a)const { return w < a.w; }}Edge;Edge eg[100100];//邻接表存图int ned[100100];//不超过x时间能访问的城市对int pre[100100];//并查集int can[100100];//集合中点数int Find(int x){ return x == pre[x]? pre[x]: (pre[x] = Find(pre[x]));}int main(){ int t,n,m,q,i,k,r,ans,x; scanf("%d",&t); while(t--) { scanf("%d %d %d",&n,&m,&q); memset(ned,0,sizeof(ned)); for(i = 1; i <= n; ++i) { pre[i] = i; can[i] = 1; } for(i = 0; i < m; ++i) scanf("%d %d %d",&eg[i].u,&eg[i].v,&eg[i].w); sort(eg,eg+m); for(i = 0; i < m; ++i) { k = Find(eg[i].v); r = Find(eg[i].u); if(k == r) continue;//两个点在一个集合 没有新城市对产生 if(k > r) pre[k] = r;//始终以小节点为集合祖先 else pre[r] = k; ned[eg[i].w] += can[r]*can[k]*2;//以w时间能访问的城市对 can[k] = can[r] += can[k];//两集合合并后生成新集合为两集合点数和 } ans = 0; for(i = 1; i <= 100000; ++i)//更新数组为不超过i时间能访问的最多城市对 { ans += ned[i]; ned[i] = ans; } while(q--) { scanf("%d",&x); printf("%d\n",ned[x]); } } return 0;}
【1007】 The Water Problem(RMQ/线段树)
The Water Problem
Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 337 Accepted Submission(s): 275
3110011 151 2 3 4 551 21 32 43 43 531 999999 141 11 22 33 3
1002344519999999999991
RMQ模板 当时脑残了 RMQ写搓了 紧张之下猛敲线段树TOT 对不起队友……(楠神还特意提醒 用线段树吧)
回来用RMQ重敲了遍 一发过 还是不熟练啊 要不比赛时就不会卡壳了……
代码如下:
#include <bits/stdc++.h>using namespace std;int rmq[1111][10];int main(){ int t,n,q,i,j,k,l,r; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i = 1; i <= n; ++i) scanf("%d",&rmq[i][0]); for(j = 1; 1<<j <= n; ++j) for(i = 1; i+(1<<j)-1 <= n; ++i) rmq[i][j] = max(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]); scanf("%d",&q); while(q--) { scanf("%d %d",&l,&r); k = log10(r-l+1)/log10(2); printf("%d\n",max(rmq[l][k],rmq[r-(1<<k)+1][k])); } } return 0;}
Elven Postman
Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 352 Accepted Submission(s): 187
So, as a elven postman, it is crucial to understand how to deliver the mail to the correct room of the tree. The elven tree always branches into no more than two paths upon intersection, either in the east direction or the west. It coincidentally looks awfully like a binary tree we human computer scientist know. Not only that, when numbering the rooms, they always number the room number from the east-most position to the west. For rooms in the east are usually more preferable and more expensive due to they having the privilege to see the sunrise, which matters a lot in elven culture.
Anyways, the elves usually wrote down all the rooms in a sequence at the root of the tree so that the postman may know how to deliver the mail. The sequence is written as follows, it will go straight to visit the east-most room and write down every room it encountered along the way. After the first room is reached, it will then go to the next unvisited east-most room, writing down every unvisited room on the way as well until all rooms are visited.
Your task is to determine how to reach a certain room given the sequence written on the root.
For instance, the sequence 2, 1, 4, 3 would be written on the root of the following tree.
For each test case, there is a number
On the next line, there is a number
Note that for simplicity, we assume the postman always starts from the root regardless of the room he had just visited.
242 1 4 331 2 366 5 4 3 2 111
EWEEEEEE
代码如下:
#include <bits/stdc++.h>using namespace std;typedef struct Node Node;typedef struct Node* Nd;struct Node{ Nd lchild,rchild; int data;};void Add(Nd tr,int x){ Nd q = (Nd)malloc(sizeof(Node)); Nd t = tr; q->data = x; q->lchild = q->rchild = NULL; while(1) { if(x > t->data)//x大于当前节点 往右子树加 { if(!t->rchild)//访问到叶子 加到右孩子 { t->rchild = q; return; } t = t->rchild; } else//否则 往左子树加 { if(!t->lchild)//访问到叶子 加到左孩子 { t->lchild = q; return; } t = t->lchild; } }}void Search(Nd tr,int x){ Nd t = tr; while(t->data != x) { if(x > t->data) { putchar('W'); t = t->rchild; } else { putchar('E'); t = t->lchild; } }}int main(){ int t,n,i,x,q; scanf("%d",&t); while(t--) { scanf("%d",&n); Nd tr = (Nd)malloc(sizeof(Node)); scanf("%d",&tr->data); tr->lchild = tr->rchild = NULL;//初始化根节点 for(i = 1; i < n; ++i) { scanf("%d",&x); Add(tr,x);//加点 } scanf("%d",&q); while(q--) { scanf("%d",&x); Search(tr,x);//查x puts(""); } } return 0;}
【1006】 Favorite Donut (KMP+最小表示法)
Favorite Donut
Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 2015 Accepted Submission(s): 489
Once Lulu eats a part of the donut, she must continue to eat its uneaten adjacent part until all parts are eaten. Therefore, she has to eat either clockwise or counter-clockwise after her first bite, and there are
For each test case, the first line contains one integer
24abab4aaab
2 04 0
拖到这么晚才来补题。。题意就是找一个同构串 让它字典序最大 这个串可以从原串任意位置出发 顺时针或逆时针转一圈
把串拷出来一个逆串 或者string直接用reserve方法 正逆都跑一遍最大表示 改一下最小表示的比较符号即可 正串找到的就是最左端 逆串找到的是逆串最左端 也就是正串最右端 用KMP算出正串最小循环节(len-next[len])%len == 0? len/next[len]: 1; 对最小循环节取余即可
需要注意的是如果两个方向得到的串字典序一样 输出下标靠左的 如果下标一样 输出顺时针的
关于最小(大)表示法 善用百度。。
代码如下:
#include <bits/stdc++.h>using namespace std;char st1[23333],st2[23333];int Maximze(char *s,int len)//最大表示法{ int i,j,k,t; i = k = 0; j = 1; while(i < len && j < len && k < len) { t = s[(j+k)%len] - s[(i+k)%len]; if(!t) ++k; else { if(t > 0) i = i+k+1; else j = j+k+1; if(i == j) ++j; k = 0; } } return min(i,j);}int cmp(char *str1,int s1,char *str2,int s2,int len)//两方向字典序比较{ for(int i = 0; i < len; ++i, s1 = (s1+1)%len, s2 = (s2+1)%len) { if(str1[s1] > str2[s2]) return 1; else if(str1[s1] < str2[s2]) return -1; } return 0;}int Next[23333];void Get(char *s,int len)//KMP{ int i,j; j = Next[0] = -1; i = 1; while(i < len) { while(j != -1 && s[i] != s[j]) { j = Next[j]; } Next[i] = ++j; ++i; }}//void Print(int len)//{// for(int i = 0; i < len; ++i) printf("%d ",Next[i]);// puts("");//}int Solve(char *s,int len){ Get(s,len); if(len%(len-Next[len-1])) return len; else return len-Next[len-1];}int main(){ int t,n,a,b,c,k; scanf("%d",&t); while(t--) { scanf("%d %s",&n,st1); for(int i = 0; st1[i]; ++i)//求逆串 st2[n-i-1] = st1[i]; st2[n] = 0; //puts(st2); a = Maximze(st1,n);//正串最大表示 b = Maximze(st2,n);//逆串最大表示 //printf("%d %d\n",a,b); k = cmp(st2,b,st1,a,n);//比较两个最大表示字典序 1:b>a -1:b<a 0:a=b if(k > 0) { printf("%d 1\n",(n-b-1)%Solve(st1,n)+1); //Print(n); } else if(k < 0) { printf("%d 0\n",a+1); } else { b = (n-b-1)%Solve(st1,n)+1; a++; if(a <= b) printf("%d 0\n",a); else printf("%d 1\n",b); //Print(n); } } return 0;}
【1003】 Aggregated Counting (找规律+打表)
Aggregated Counting
Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 508 Accepted Submission(s): 226
The sequence is generated by the following scheme.
1. First, write down 1, 2 on a paper.
2. The 2nd number is 2, write down 2 2’s (including the one originally on the paper). The paper thus has 1, 2, 2 written on it.
3. The 3rd number is 2, write down 2 3’s. 1, 2, 2, 3, 3 is now shown on the paper.
4. The 4th number is 3, write down 3 4’s. 1, 2, 2, 3, 3, 4, 4, 4 is now shown on the paper.
5. The procedure continues indefinitely as you can imagine. 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, . . . .
The ICPC is widely renowned for its counting ability. At ACM2016, they came up with all sorts of intriguing problems in regard to this sequence, and here is one: Given a positive number
3310100000
11217507231491
新开一个数组ans[i] 表示last[last[i]]影响的区间的上限
1 [1,1] 1]
2 [2,3] 11]
3 [4,5] 38]
4 [6,8] 122
5 [9,11] 272
6 [12,15] 596
可以得出 ans[i] = ans[i-1]+∑(last[i-1],last[i]]*i 即ans存的就是last[i] 最后一次出现的位置
这样last[i]表示受第i个数影响的区间 ans表示该区间映射到的受影响区间 找出规律就很好打表了
每次给个n二分找到他所在的last区间 然后从对应ans[pos]递减出n对应的答案即可
代码如下:
#include <bits/stdc++.h>#define ll long long#define mod 1000000007using namespace std;int ans[500000];int post[500000];int tp;ll cal(ll a)//找∑(1~a){ return a&1? (a+1)/2*a: a/2*(a+1);}int binsearch(int l,int r,int x)//二分找第一个大于等于x的区间{ int ans; while(l <= r) { int mid = (l+r)>>1; if(x <= post[mid]) { ans = mid; r = mid-1; } else l = mid+1; } return ans;}int main(){ post[1] = 1; ans[1] = 1; post[2] = 3; ans[2] = 5; tp = 3; for(int i = 2, j = 3; i < tp; ++i) { while(j <= post[i] && post[tp-1] < 1000000000)//n <= 1e9 所以只需找到post[tp] <= 1e9即可 i影响区间[j,post[i]] { post[tp] = post[tp-1]+i; ++j; ++tp; } ans[i] = (ans[i-1]+(((cal(post[i])-cal(post[i-1]))%mod)*i)%mod)%mod;//ans[i] = ans[i-1] + (∑(1~post[i])-∑(1~post[i]))*i } int t,n,id; scanf("%d",&t); while(t--) { scanf("%d",&n); id = binsearch(1,tp-1,n);//二分找到第一个大于等于n的区间 printf("%d\n",(ans[id]-((cal(post[id])-cal(n))*id)%mod+mod)%mod);//ans[i] - (∑(1~post[i])-∑(1~n)) 即为n对应的答案 } return 0;}
- 【题解】 2015 ACM/ICPC Asia Regional Changchun Online (5+2)
- 2015 ACM/ICPC Asia Regional Changchun Online
- 2015 ACM/ICPC Asia Regional Changchun Online
- 2012 ACM/ICPC Asia Regional Changchun Online
- 2013 ACM/ICPC Asia Regional Changchun Online
- 2013 ACM/ICPC Asia Regional Changchun Online
- 2013 ACM/ICPC Asia Regional Changchun Online
- 2015 ACM/ICPC Asia Regional Changchun Online(1002)
- 2015长春网络赛 ACM/ICPC Asia Regional Changchun Online
- 2015 ACM/ICPC Asia Regional Changchun Online(1007)
- 2015 ACM/ICPC Asia Regional Changchun Online hdu 5438
- hdu5443(2015 ACM/ICPC Asia Regional Changchun Online )
- Alisha’s Party--2015 ACM/ICPC Asia Regional Changchun Online
- 2015 ACM/ICPC Asia Regional Changchun Online(1001)
- 2015 ACM/ICPC Asia Regional Changchun Online(1008)
- 2015 ACM/ICPC Asia Regional Changchun Online(1005)
- 2015 ACM/ICPC Asia Regional Changchun Online (部分)
- 2015 ACM/ICPC Asia Regional Changchun Online hdu5444
- 文章标题
- 数据落地不落地导入导出的一个误区
- T-SQL 中的CROSS JOIN用法(半翻译)
- javascript无法对com返回的64位整数比较大小
- 解决Eclipse发布webproject, jar无法发布到tomcat lib目录下的问题
- 【题解】 2015 ACM/ICPC Asia Regional Changchun Online (5+2)
- Apache Thrift的简单使用
- android Json解析详解(详细代码)
- 用OpenSSL编写SSL,TLS程序
- dpkg 和 apt-get的使用
- 我的2年苦逼狗血相亲经历(7)
- Web前端性能优化整理
- Matlab自带排序函数sort用法
- java后台实现日期转换成相应的格式