魔兽争霸 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
小x正在销魂地玩魔兽
他正控制着死亡骑士和n个食尸鬼(编号1~n)去打猎死亡骑士有个魔法,叫做“死亡缠绕”,可以给食尸鬼补充HP战斗过程中敌人会对食尸鬼实施攻击,食尸鬼的HP会减少,小x希望随时知道自己部队的情况,即HP值第k多的食尸鬼有多少HP,以便决定如何施放魔法.请同学们帮助他:) 小x向你发出3种信号:(下划线在输入数据中表现为空格) A_i_a表示敌军向第i个食尸鬼发出了攻击,并使第i个食尸鬼损失了a点HP,如果它的HP<=0,那么这个食尸鬼就死了(Undead也是要死的……)。 敌军不会攻击一个已死的食尸鬼。 C_i_a 表示死亡骑士向第i个食尸鬼放出了死亡缠绕,并使其增加了a点HP。 HP值没有上限。 死亡骑士不会向一个已死的食尸鬼发出死亡缠绕 Q_k 表示小x向你发出询问 |
输入
|
第一行,一个正整数 n
以后n个整数 表示n个食尸鬼的初始HP值 接着一个正整数m 以下m行 每行一个小x发出的信号 |
输出
|
对于小x的每个询问,输出HP第k多的食尸鬼有多少HP,如果食尸鬼总数不足k个,输出-1。每个一行数。
最后一行输出一个数:战斗结束后剩余的食尸鬼数 |
输入示例
|
5
1 2 3 4 5 10 Q 2 A 4 6 C 1 4 Q 2 A 2 1 A 3 3 A 1 3 Q 4 C 2 10 Q 1 |
输出示例
|
4
5 -1 11 3 |
其他说明
|
数据范围:
40%的数据 n<=3000 m<=5000 70%的数据 n<=8000 m<=10000 100%的数据 n<=30000 m<=50000 90%的数据随机生成 食尸鬼HP没有上限 数据保证任意时刻食尸鬼的HP值在longint范围内 数据保证A和C命令中的食尸鬼是活着的 输入数据中没有多余空格、换行 |
东风谷早苗 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
在幻想乡,东风谷早苗是以高达控闻名的高中生宅巫女。某一天,早苗终于入手了最新款的钢达姆模型。作为最新的钢达姆,当然有了与以往不同的功能了,那就是它能够自动行走,厉害吧(好吧,我自重)。早苗的新模型可以按照输入的命令进行移动,命令包含’E’、’S’、’W’、’N’四种,分别对应四个不同的方向,依次为东、南、西、北。执行某个命令时,它会向着对应方向移动一个单位。作为新型机器人,自然不会只单单执行一个命令,它可以执行命令串。对于输入的命令串,每一秒它会按照命令行动一次。而执行完命令串最后一个命令后,会自动从头开始循环。在0时刻时早苗将钢达姆放置在了(0,0)的位置,并且输入了命令串。她想要知道T秒后钢达姆所在的位置坐标。
|
输入
|
第1行:一个字符串,表示早苗输入的命令串,保证至少有1个命令
第2行:一个正整数T |
输出
|
第1行:两个整数,表示T秒时,钢达姆的坐标
|
输入示例
|
NSWWNSNEEWN
12 |
输出示例
|
-1 3
|
其他说明
|
数据范围:
对于60%的数据:T <= 500,000且命令串长度 <= 5,000 对于100%的数据:T <= 2,000,000,000且命令串长度<= 5,000 |
。。。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=5010; char s[maxn]; int main() { scanf("%s",s);int n=strlen(s); int T=read(); int x0=0,y0=0; rep(i,0,n-1) { if(s[i]=='W') x0--; if(s[i]=='E') x0++; if(s[i]=='N') y0++; if(s[i]=='S') y0--; } int x=x0*(T/n),y=y0*(T/n); rep(i,0,(T%n)-1) { if(s[i]=='W') x--; if(s[i]=='E') x++; if(s[i]=='N') y++; if(s[i]=='S') y--; } printf("%d %d ",x,y); return 0; }
西行寺幽幽子 |
难度级别:C; 运行时间限制:3000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
在幻想乡,西行寺幽幽子是以贪吃闻名的亡灵。不过幽幽子可不是只会吃,至少她还管理着亡灵界。话说在幽幽子居住的白玉楼有一颗常年不开花的樱树——西行妖。幽幽子决定去收集人间的春度,聚集起来让西行妖开花。很快,作为幽幽子家园艺师的魂魄妖梦收集到了M个单位的春度。并且在这段时间里,幽幽子计算出要让西行妖开出一朵花需要N个单位的春度。现在幽幽子想要知道,使用所有的春度,能够让西行妖开出多少朵花。 |
输入
|
第1行:一个正整数M
第2行:一个正整数N N,M的位数不超过L,L的范围在题目后面给出 |
输出
|
第1行:一个整数ans,表示能开出花的朵数
|
输入示例
|
73861758
12471 |
输出示例
|
5922
|
其他说明
|
数据范围:对于60%的数据:L <= 2,000且ans <= 2,000,对于100%的数据:L <= 20,000且ans <= 2,000,000,000
|
二分答案,高精度判一判
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=21000; typedef long long ll; struct bign { int len; ll c[maxn]; void clean() { while(len>1&&!c[len-1]) len--; } void operator = (const char* s) { len=strlen(s);memset(c,0,sizeof(c)); rep(i,0,len-1) c[i]=s[len-i-1]-'0'; } void operator *= (const int& b) { len+=12; rep(i,0,len-1) c[i]*=b; rep(i,0,len-2) c[i+1]+=c[i]/10,c[i]%=10; clean(); } bool operator <= (const bign& b) const { if(len>b.len) return 0; if(len<b.len) return 1; dwn(i,len-1,0) { if(c[i]>b.c[i]) return 0; if(c[i]<b.c[i]) return 1; } return 1; } void print() { dwn(i,len-1,0) printf("%lld",c[i]); puts(""); } }; char s[maxn]; int main() { bign n,m; scanf("%s",s);m=s; scanf("%s",s);n=s; if(n<=m) { int l=0,r=2000000001; while(l+1<r) { int mid=l+(r-l>>1); bign t=n;t*=mid; if(t<=m) l=mid; else r=mid; } printf("%d ",l); } else puts("0"); return 0; }
琪露诺 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
在幻想乡,琪露诺是以笨蛋闻名的冰之妖精。某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来。但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸。于是琪露诺决定到河岸去追青蛙。小河可以看作一列格子依次编号为0到N,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子i时,她只会移动到i+L到i+R中的一格。你问为什么她这么移动,这还不简单,因为她是笨蛋啊。每一个格子都有一个冰冻指数A[i],编号为0的格子冰冻指数为0。当琪露诺停留在那一格时就可以得到那一格的冰冻指数A[i]。琪露诺希望能够在到达对岸时,获取最大的冰冻指数,这样她才能狠狠地教训那只青蛙。但是由于她实在是太笨了,所以她决定拜托你帮它决定怎样前进。开始时,琪露诺在编号0的格子上,只要她下一步的位置编号大于N就算到达对岸。
|
输入
|
第1行:3个正整数N, L, R
第2行:N+1个整数,第i个数表示编号为i-1的格子的冰冻指数A[i-1] |
输出
|
第1行:一个整数,表示最大冰冻指数。保证不超过2^31-1
第2行:空格分开的若干个整数,表示琪露诺前进的路线,最后输出-1表示到达对岸 |
输入示例
|
5 2 3
0 12 3 11 7 -2 |
输出示例
|
11
0 3 -1 |
其他说明
|
数据范围:对于60%的数据:N <= 10,000,对于100%的数据:N <= 200,000,对于所有数据 -1,000 <= A[i] <= 1,000且1 <= L <= R <= N
|
设f[i]表示前i个格子,现在在第i个格子上的最大值。
用一个单调队列维护一下区间最大值,打印方案的话可以用个数组记一下。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long ll; const int maxn=200010; const int INF=1000000000; int n,L,R,A[maxn],q[maxn],f[maxn],g[maxn]; int S[maxn],top; int main() { n=read();L=read();R=read(); rep(i,0,n) A[i]=read(); f[0]=0; int l=1,r=0; rep(i,1,n) { if(i>=L) { while(l<=r&&f[q[r]]<=f[i-L]) r--; q[++r]=i-L; } while(l<=r&&q[l]<i-R) l++; if(l<=r) f[i]=f[q[l]]+A[i],g[i]=q[l]; else f[i]=-INF; } int ans=-INF,p; rep(i,0,n) if(i+R>n&&f[i]>ans) { ans=f[i];p=i; } printf("%d ",ans); while(p) S[++top]=p,p=g[p]; printf("0");while(top) printf(" %d",S[top--]); puts(" -1"); return 0; }
上白泽慧音 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为<A,B>。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足<X,Y>。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。
|
输入
|
第1行:两个正整数N,M
第2..M+1行:每行三个正整数a,b,t, t = 1表示存在从村庄a到b的单向道路,t = 2表示村庄a,b之间存在双向通行的道路。保证每条道路只出现一次。 |
输出
|
第1行: 1个整数,表示最大的绝对连通区域包含的村庄个数。
第2行:若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。 |
输入示例
|
5 5
1 2 1 1 3 2 2 4 2 5 1 2 3 5 1 |
输出示例
|
3
1 3 5 |
其他说明
|
数据范围:对于60%的数据:N <= 200且M <= 10,000,对于100%的数据:N <= 5,000且M <= 50,000
|
裸的强连通分量
#include<cstdio> #include<cctype> #include<stack> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=5010; const int maxm=100010; stack<int> S; int first[maxn],next[maxm],to[maxm],e; int pre[maxn],low[maxn],num[maxn],tot[maxn],mn[maxn],dfs_clock,scc_cnt; void AddEdge(int u,int v) { to[++e]=v;next[e]=first[u];first[u]=e; } int dfs(int x) { pre[x]=low[x]=++dfs_clock;S.push(x); ren { if(!pre[to[i]]) low[x]=min(low[x],dfs(to[i])); else if(!num[to[i]]) low[x]=min(low[x],pre[to[i]]); } if(pre[x]==low[x]) { scc_cnt++;mn[scc_cnt]=1<<30; for(;;) { int c=S.top();S.pop(); num[c]=scc_cnt;tot[scc_cnt]++; mn[scc_cnt]=min(c,mn[scc_cnt]); if(c==x) break; } } return low[x]; } int main() { int n=read(),m=read(); rep(i,1,m) { int u=read(),v=read(),t=read(); AddEdge(u,v); if(t==2) AddEdge(v,u); } rep(i,1,n) if(!pre[i]) dfs(i); int ans,mx=0,f=0; rep(i,1,scc_cnt) if(tot[i]>mx||(tot[i]==mx&&mn[i]<mn[ans])) { ans=i;mx=tot[i]; } printf("%d ",mx); rep(i,1,n) if(num[i]==ans) { if(!f) f=1; else putchar(' '); printf("%d",i); } return 0; }
环上的游戏 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
有一个取数的游戏。初始时,给出一个环,环上的每条边上都有一个非负整数。这些整数中至少有一个0。然后,将一枚硬币放在环上的一个节点上。两个玩家就是以这个放硬币的节点为起点开始这个游戏,两人轮流取数,取数的规则如下: 现在,你的任务就是根据给出的环、边上的数值以及起点(硬币所在位置),判断先走方是否有必胜的策略。 |
输入
|
第一行一个整数N(N≤20),表示环上的节点数。
第二行N个数,数值不超过30,依次表示N条边上的数值。硬币的起始位置在第一条边与最后一条边之间的节点上。 |
输出
|
仅一行。若存在必胜策略,则输出“YES”,否则输出“NO”。
|
输入示例
|
4
2 5 3 0 |
输出示例
|
YES
|
注意条件“这些整数中至少有一个0。”
那么其实是链上的问题,只要判起点到左端和右端是否有一个距离为奇数
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int A[25],n; int main() { n=read(); rep(i,1,n) A[i]=read(); int r=n,l=0; while(A[r]) r--; while(A[l+1]) l++; r=n-r; if(l&1||r&1) puts("YES"); else puts("NO"); return 0; }
舞蹈课 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
有n个人参加一个舞蹈课。每个人的舞蹈技术由整数来决定。在舞蹈课的开始,他们从左到右站成一排。当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出列并开始跳舞。如果相差最小的不止一对,那么最左边的那一对出列。一对异性出列之后,队伍中的空白按原顺序补上(即:若队伍为ABCD,那么BC出列之后队伍变为AD)。舞蹈技术相差最小即是的绝对值最小。 你的任务是,模拟以上过程,确定跳舞的配对及顺序。 |
输入
|
第一行为正整数n<=200000:队伍中的人数。下一行包含n个字符B或者G,B代表男,G代表女。下一行为n个整数ai<=10^7。所有信息按照从左到右的顺序给出。在50%的数据中,n<=200。
|
输出
|
第一行:出列的总对数k。接下来输出k行,每行是两个整数。按跳舞顺序输出,两个整数代表这一对舞伴的编号(按输入顺序从左往右1至n编号)。请先输出较小的整数,再输出较大的整数。
|
输入示例
|
4
BGBG 4 2 4 3 |
输出示例
|
2
3 4 1 2 |
其他说明
|
补充样例:
输入样例 4 BGBB 1 1 2 3 输出样例 1 1 2 |
用一个链表来维护相互关系,用一个对来维护有关系的数对。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } struct Pair { int pos,v; bool operator < (const Pair& ths) const { return v>ths.v||(v==ths.v&&pos>ths.pos); } }; priority_queue<Pair> Q; const int maxn=200010; int n,A[maxn],type[maxn],Left[maxn],Right[maxn]; int del[maxn]; int ans1[maxn],ans2[maxn],top; char s[maxn]; int main() { n=read();scanf("%s",s+1); rep(i,1,n) { Left[i]=i-1;Right[i]=i+1; A[i]=read();type[i]=s[i]=='G'; } rep(i,1,n-1) if(type[i]!=type[i+1]) Q.push((Pair){i,abs(A[i]-A[i+1])}); while(!Q.empty()) { Pair x=Q.top();int pos=x.pos;Q.pop(); if(Right[pos]>n||del[pos]||del[Right[pos]]) continue; if(type[pos]==type[Right[pos]]||abs(A[pos]-A[Right[pos]])!=x.v) continue; //printf("%d %d ",pos,x.v); del[pos]=del[Right[pos]]=1; ans1[++top]=pos;ans2[top]=Right[pos]; if(Left[pos]&&Right[Right[pos]]<=n) if(type[Left[pos]]!=type[Right[Right[pos]]]) Q.push((Pair){Left[pos],abs(A[Left[pos]]-A[Right[Right[pos]]])}); Left[Right[Right[pos]]]=Left[pos]; Right[Left[pos]]=Right[Right[pos]]; } printf("%d ",top); rep(i,1,top) printf("%d %d ",ans1[i],ans2[i]); return 0; }
栅栏迷宫 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
田野上搭建了一个黄金大神专用的栅栏围成的迷宫。幸运的是,在迷宫的边界上留出了两段栅栏作为迷宫的出口。更幸运的是,所建造的迷宫是一个“完美的”迷宫:即你能从迷宫中的任意一点找到一条走出迷宫的路。给定迷宫的宽W(1<=W<=38)及长H(1<=H<=100)。 2*H+1行,每行2*W+1的字符以下面给出的格式表示一个迷宫。然后计算从迷宫中最“糟糕”的那一个点走出迷宫所需的步数(就是从最“糟糕”的一点,走出迷宫的最少步数)。(即使从这一点以最优的方式走向最靠近的出口,它仍然需要最多的步数)当然了,黄金大神让你必须只会水平或垂直地在X或Y轴上移动,你不能从来不走对角线。每移动到一个新的方格算作一步(包括移出迷宫的那一步)这是一个W=5,H=3的迷宫: |
输入
|
第一行: W和H(用空格隔开)
第二行至第2*H+1行: 每行2*W+1个字符表示迷宫 |
输出
|
输出一个单独的整数,表示能保证牛从迷宫中任意一点走出迷宫的最小步数。
|
输入示例
|
5 3
+-+-+-+-+-+ | | +-+ +-+ + + | | | | + +-+-+ + + | | | +-+ +-+-+-+ |
输出示例
|
9
|
。。。
#include<cstdio> #include<cctype> #include<queue> #include<iostream> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=210; const int mx[]={1,-1,0,0}; const int my[]={0,0,1,-1}; char A[maxn][maxn]; struct Point { int x,y; }; queue<Point> Q; int n,m,ans,d[maxn][maxn],vis[maxn][maxn]; int main() { m=read()<<1|1,n=read()<<1|1; rep(i,1,n) cin.getline(A[i]+1,1000); rep(i,1,n) rep(j,1,m) if(i==1||j==1||i==n||j==m) { if(A[i][j]==' ') { int x=i,y=j;if(i==1) x++;if(i==n) x--; if(j==1) y++;if(j==m) y--; vis[x][y]=1;d[x][y]=1;Q.push((Point){x,y}); } } while(!Q.empty()) { int x=Q.front().x,y=Q.front().y;Q.pop(); ans=max(ans,d[x][y]); rep(dir,0,3) { int nx=x+mx[dir],ny=y+my[dir]; if(nx+mx[dir]>=1&&nx+mx[dir]<=n&&ny+my[dir]>=1&&ny+my[dir]<=m&&A[nx][ny]==' ') if(!vis[nx+mx[dir]][ny+my[dir]]) { nx+=mx[dir];ny+=my[dir]; d[nx][ny]=d[x][y]+1; vis[nx][ny]=1; Q.push((Point){nx,ny}); } } } printf("%d ",ans); return 0; } /* 5 3 +-+-+-+-+-+ | | +-+ +-+ + + | | | | + +-+-+ + + | | | +-+ +-+-+-+ */
人偶师 |
难度级别:C; 运行时间限制:3000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
n点m双向边的图,每个点有2个状态:开和关。每次操作改变一个点的状态,以及与其有边直接相连的点的状态。问开启所有点至少需要多少次操作。 |
输入
|
第一行2个整数n,m。
第二行n个整数,第i个数表示第i点的状态,0为关,1为开。 第3..m+2行,每行2个整数a,b,表示a和b直接相连,同一条边不会出现多次。 |
输出
|
第一行一个整数k表示最少的操作次数,所有数据保证至少有一组可行解。
|
输入示例
|
4 3
1 1 0 0 2 3 1 3 2 4 |
输出示例
|
3
|
其他说明
|
数据范围:
对于30%的数据,1<=n<=10,0<=m<=40 对于60%的数据,1<=n<=30,0<=m<=100 对于100%的数据,1<=n<=40,0<=m<=500 |
看到n<=40,不难想到中途相遇。分别枚举前一半点、后一半点,用哈希表判判即可。
#include<cstdio> #include<cctype> #include<queue> #include<map> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[pos];i;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long ll; const int maxn=45; const int maxc=1<<21; const int HASH=934247; ll M[maxc],val[maxc],now; int val2[maxc],next[maxc],first[HASH],cnt; void insert(ll p,int v) { int pos=p%HASH; val[++cnt]=p;val2[cnt]=v;next[cnt]=first[pos];first[pos]=cnt; } int query(ll p) { int pos=p%HASH,ans=100; ren if(val[i]==p) ans=min(ans,val2[i]); return ans; } int n,m,ans; int main() { n=read();m=read();ans=n+1; rep(i,0,n-1) { M[i]=1ll<<i; if(read()) now|=1ll<<i; } rep(i,1,m) { int x=read()-1,y=read()-1; M[x]|=1ll<<y;M[y]|=1ll<<x; } int n0=n/2,n1=n-n0; rep(S,0,(1<<n0)-1) { ll t=0;int cnt=0; rep(i,0,n0-1) if(S>>i&1) t^=M[i],cnt++; insert(t,cnt); } rep(S,0,(1<<n1)-1) { ll t=0;int cnt=0; rep(i,0,n1-1) if(S>>i&1) t^=M[i+n0],cnt++; ans=min(ans,cnt+query(((1ll<<n)-1)^t^now)); } printf("%d ",ans); return 0; }
某种密码 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
关于某种密码有如下描述:某种密码的原文A是由N个数字组成,而密文B是一个长度为N的01数串,原文和密文的关联在于一个钥匙码KEY。若KEY=∑〖Ai*Bi〗,则密文就是原文的一组合法密码。 现在有原文和钥匙码,请编一个程序来帮助他统计到底有多少个符合条件的密文。 |
输入
|
第一行两个数N,KEY,意义同题目描述;
第二行N个数表示原文A,意义同题目描述。 |
输出
|
一个数ANS,表示对于原文A和KEY,有多少组可行的密文B。
|
输入示例
|
3 2
1 1 2 |
输出示例
|
2
|
其他说明
|
数据范围:60%数据满足N<=25,100%数据满足N<=40,-maxlongint<=∑Ai<=maxlongint
|
又是一道中途相遇。。。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[pos];i;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=45; const int maxc=1<<21; const int HASH=934247; int n,m,A[maxn],val[maxc],tot[maxn],next[maxc],first[HASH],cnt; void insert(int p) { int pos=p%HASH; ren if(val[i]==p) {tot[i]++;return;} val[++cnt]=p;tot[cnt]=1;next[cnt]=first[pos];first[pos]=cnt; } int query(int p) { int pos=p%HASH; ren if(val[i]==p) return tot[i]; return 0; } int main() { n=read();m=read();long long ans=0; rep(i,0,n-1) A[i]=read(); int n0=n/2,n1=n-n0; rep(S,0,(1<<n0)-1) { int t=0; rep(i,0,n0-1) if(S>>i&1) t+=A[i]; insert(t); } rep(S,0,(1<<n1)-1) { int t=0; rep(i,0,n1-1) if(S>>i&1) t+=A[i+n0]; ans+=query(m-t); } printf("%lld ",ans); return 0; }
球的序列 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
N个编号为1-n的球,每个球都有唯一的编号。这些球被排成两种序列,分别为A、B序列,现在需要重新寻找一个球的序列l,对于这个子序列l中任意的两个球,要求j,k(j<k),都要求满足lj在A中位置比lk在A中位置靠前,且lj在B中位置比lk在B中位置靠前,请你计算这个子序列l的最大长度。
|
输入
|
第一行一个整数,表示N。
第二行N个整数,表示A序列。 第三行N个整数,表示B序列。 |
输出
|
输出一个数
|
输入示例
|
5
1 2 4 3 5 5 2 3 4 1 |
输出示例
|
2
|
其他说明
|
数据范围:40% N<=5000,100% N<=50000
|
将A序列的每一个数在B中的位置kuai出来,做一个LIS即可。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=50010; const int INF=1000000000; int n,ans,A[maxn],C[maxn],g[maxn]; int main() { n=read(); rep(i,1,n) A[read()]=i; rep(i,1,n) C[i]=A[read()],g[i]=INF; rep(i,1,n) { int k=lower_bound(g+1,g+n+1,C[i])-g; ans=max(k,ans);g[k]=C[i]; } printf("%d ",ans); return 0; }
大逃亡 |
难度级别:C; 运行时间限制:2000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
给出数字N(1<=N<=10000),X(1<=x<=1000),Y(1<=Y<=1000),代表有N个敌人分布一个X行Y列的矩阵上,矩形的行号从0到X-1,列号从0到Y-1再给出四个数字x1,y1,x2,y2,代表你要从点(x1,y1)移到(x2,y2)。在移动的过程中你当然希望离敌人的距离的最小值最大化,现在请求出这个值最大可以为多少,以及在这个前提下,你最少要走多少步才可以回到目标点。注意这里距离的定义为两点的曼哈顿距离,即某两个点的坐标分为(a,b),(c,d),那么它们的距离为|a-c|+|b-d|。
|
输入
|
第一行给出数字N,X,Y
第二行给出x1,y1,x2,y2 下面将有N行,给出N个敌人所在的坐标 |
输出
|
在一行内输出你离敌人的距离及在这个距离的限制下,你回到目标点最少要移动多少步。
|
输入示例
|
2 5 6
0 0 4 0 2 1 2 3 |
输出示例
|
2 14
|
先BFS一遍算出距离每个位置最近的敌人的距离,再二分+BFS判定。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=1010; const int mx[]={1,-1,0,0}; const int my[]={0,0,1,-1}; int n,m,d[maxn][maxn],T[maxn][maxn],vis[maxn][maxn]; queue<int> Q; void caltime() { while(!Q.empty()) { int x=Q.front()/m,y=Q.front()%m;Q.pop(); rep(dir,0,3) { int nx=x+mx[dir],ny=y+my[dir]; if(nx>=0&&nx<n&&ny>=0&&ny<m&&!vis[nx][ny]) { vis[nx][ny]=1;T[nx][ny]=T[x][y]+1; Q.push(nx*m+ny); } } } memset(vis,0,sizeof(vis)); } int check(int mid,int x1,int x2,int y1,int y2) { if(T[x1][y1]<mid) return 0; memset(vis,0,sizeof(vis)); Q.push(x1*m+y1);vis[x1][y1]=1;d[x1][y1]=0; while(!Q.empty()) { int x=Q.front()/m,y=Q.front()%m;Q.pop(); rep(dir,0,3) { int nx=x+mx[dir],ny=y+my[dir]; if(nx>=0&&nx<n&&ny>=0&&ny<m&&!vis[nx][ny]&&T[nx][ny]>=mid) { vis[nx][ny]=1;d[nx][ny]=d[x][y]+1; Q.push(nx*m+ny); } } } return vis[x2][y2]; } int main() { int c=read();n=read();m=read(); int x1=read(),y1=read(),x2=read(),y2=read(); rep(i,1,c) { int x=read(),y=read(); T[x][y]=0;vis[x][y]=1;Q.push(x*m+y); } caltime(); int l=0,r=max(n,m)+1,mid; while(l+1<r) if(check(mid=l+r>>1,x1,x2,y1,y2)) l=mid; else r=mid; check(l,x1,x2,y1,y2);printf("%d %d ",l,d[x2][y2]); return 0; }
祖孙询问 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
已知一棵n个节点的有根树。有m个询问。每个询问给出了一对节点的编号x和y,询问x与y的祖孙关系。
|
输入
|
输入第一行包括一个整数n表示节点个数。
接下来n行每行一对整数对a和b表示a和b之间有连边。如果b是-1,那么a就是树的根。 第n+2行是一个整数m表示询问个数。 接下来m行,每行两个正整数x和y。 |
输出
|
对于每一个询问,输出1:如果x是y的祖先,输出2:如果y是x的祖先,否则输出0。
|
输入示例
|
10
234 -1 12 234 13 234 14 234 15 234 16 234 17 234 18 234 19 234 233 19 5 234 233 233 12 233 13 233 15 233 19 |
输出示例
|
1
0 0 0 2 |
其他说明
|
数据范围:对于30%的数据,n,m≤1000,对于100%的.据,n,m≤40000,每个节点的编号都不超过40000。
|
算一下DFS序,然后就可以O(1)判断祖孙关系了。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=40010; int first[maxn],next[maxn<<1],to[maxn<<1],e; void AddEdge(int u,int v) { to[++e]=v;next[e]=first[u];first[u]=e; to[++e]=u;next[e]=first[v];first[v]=e; } int L[maxn],R[maxn],cnt; void dfs(int x,int fa) { L[x]=++cnt; ren if(to[i]!=fa) dfs(to[i],x); R[x]=cnt; } int main() { int n=read(),rt; rep(i,1,n) { int x=read(),y=read(); if(y<0) rt=x; else AddEdge(x,y); } dfs(rt,0); int q=read(); while(q--) { int x=read(),y=read(); if(L[x]<=L[y]&&L[y]<=R[x]) puts("1"); else if(L[y]<=L[x]&&L[x]<=R[y]) puts("2"); else puts("0"); } return 0; }
比赛 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
有两个队伍A和B,每个队伍都有n个人。这两支队伍之间进行n场1对1比赛,每一场都是由A中的一个选手与B中的一个选手对抗。同一个人不会参加多场比赛,每个人的对手都是随机而等概率的。例如A队有A1和A2两个人,B队有B1和B2两个人,那么(A1 vs B1,A2 vs B2)和(A1 vs B2,A2 vs B1)的概率都是均等的50%。
每个选手都有一个非负的实力值。如果实力值为X和Y的选手对抗,那么实力值较强的选手所在的队伍将会获得(X-Y)^2的得分。 求A的得分减B的得分的期望值。 |
输入
|
第一行一个数n表示两队的人数为n。
第二行n个数,第i个数A[i]表示队伍A的第i个人的实力值。 第三行n个数,第i个数B[i]表示队伍B的第i个人的实力值。 |
输出
|
输出仅包含一个实数表示A期望赢B多少分。答案保留到小数点后一位(注意精度)。
|
输入示例
|
2
3 7 1 5 |
输出示例
|
20.0
|
其他说明
|
数据范围:对于30%的数据,n≤50,对于100%的.据,n≤50000;A[i],B[i]≤50000。
|
先写出式子:ans=∑((Ai-Bj)*|Ai-Bj|/n)
当Ai>Bj时ans=∑(Ai^2-2AiBj+Bj^2)/n
当Ai<Bj时ans=∑(-Ai^2+2AiBj-Bj^2)/n
那么两遍分别排序后,维护这几个值就行了。
注意要开long double
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=50010; long double A[maxn],B[maxn],ans; int main() { int n=read(); double sum=0,sum2=0,all=0,all2=0; rep(i,1,n) A[i]=read(); rep(i,1,n) B[i]=read(),all-=2*B[i],all2+=B[i]*B[i]; sort(A+1,A+n+1);sort(B+1,B+n+1); int cur=0; rep(i,1,n) { while(cur<n&&B[cur+1]<=A[i]) cur++,sum-=2*B[cur],sum2+=B[cur]*B[cur]; ans+=(A[i]*A[i]*(2.0*cur-n)+A[i]*(2.0*sum-all)+sum2*2.0-all2)/n; } double ans2=ans; printf("%.1lf ",ans2); return 0; }
数字 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
一个数字被称为好数字当他满足下列条件:
1. 它有2*n个数位,n是正整数(允许有前导0)。 2. 构成它的每个数字都在给定的数字集合S中。 3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等 例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。 已知n,求合法的好数字的个数mod 999983。 |
输入
|
第一行一个数n。
接下来一个长度不超过10的字符串,表示给定的数字集合。 |
输出
|
一行一个数字表示合法的好数字的个数mod 999983。
|
输入示例
|
2
0987654321 |
输出示例
|
1240
|
其他说明
|
数据范围;对于20%的数据,n≤7,对于100%的.据,n≤1000,|S|≤10。
|
设性质1为前n位之和与后n位之和相等,性质2为奇数位之和与偶数位之和相等
用容斥原理,计算出满足性质1,满足性质2的和性质1、2均满足的数字个数ans1,ans2,ans3。
设f[i][j]表示有i为,数字和为j的数字个数。
ans1=ans2=∑f[n][j]*f[n][j]
性质1、2均满足的数可以这么算:
设[1,n]的奇数位和为x,[1,n]的偶数位和为y。
则[n+1,2n]的奇数位和为y,[n+1,2n]的偶数位和为x。
分奇偶两种情况讨论,两者可以独立分开算。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long ll; const int maxn=1010; const int mod=999983; int n,ok[12],f[maxn][maxn*9]; char s[12]; int main() { n=read();scanf("%s",s); dwn(i,strlen(s)-1,0) ok[s[i]-'0']=1; f[0][0]=1; rep(i,1,n) rep(j,0,i*9) { int& ans=f[i][j]; dwn(k,min(9,j),0) if(ok[k]) ans+=f[i-1][j-k]; ans%=mod; } ll ans=0,ans2=0,ans3=0; rep(j,0,n*9) (ans+=(ll)f[n][j]*f[n][j])%=mod; ans=ans*2%mod; rep(i,0,n*9) (ans2+=(ll)f[n/2][i]*f[n/2][i])%=mod; rep(i,0,n*9) { if(!(n&1)) (ans3+=(ll)f[n/2][i]*f[n/2][i])%=mod; else (ans3+=(ll)f[n/2+1][i]*f[n/2+1][i])%=mod; } printf("%lld ",((ans-ans2*ans3)%mod+mod)%mod); return 0; }
锻炼计划 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
身体是革命的本钱,OIers不要因为紧张的学习和整天在电脑前而忽视了健康问题。小x设计了自己的锻炼计划,但他不知道这个计划是否可行,换句话说如果计划不当可能会让他的体力超支,所以小x请你帮助他。
一天有1440分钟,所以小x列出的是这一整天第1至第1440分钟的计划。小x的体力用一个整数来表示,他会按照计划表进行锻炼,同时,每分钟小x的体力会自动增加1。如果某一分钟末小x的体力小于等于零,那么可怜的小x就累死了…… |
输入
|
第一行是用空格分开的两个整数n,m,分别表示小x的初始体力值和计划的项目数量。
从第二行开始的m行,每行描述一个锻炼项目:名称、开始时间a、结束时间b、每分钟耗费的体力(用空格分隔),表示此项目从第a分钟初开始,第b分钟末结束。锻炼项目按照开始时间递增顺序给出,不会出现两个项目时间冲突的情况。 |
输出
|
输出包括两行,如果计划可行,第一行输出"Accepted",第二行输出这一天过后最后剩余的体力;否则在第一行输出"Runtime Error",第二行输出在第几分钟累死。
|
输入示例
|
10 1
Basketball 1 10 1 |
输出示例
|
Accepted
1440 |
其他说明
|
数据范围:0<n<=2^31-1,0<=m<=500
所有中间值的绝对值不会超过2^31-1 每一个锻炼项目的名称不超过20个字符,其中不含空格。 |
。。。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long ll; const int maxn=1500; ll A[maxn]; char s[maxn]; int main() { ll n=read(); dwn(i,read(),1) { scanf("%s",s); int s=read(),t=read(),v=read(); rep(j,s,t) A[j]+=v; } rep(i,1,1440) { n++;n-=A[i]; if(n<=0) {printf("Runtime Error %d ",i);return 0;} } printf("Accepted %lld ",n); return 0; }
小猫爬山 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
Freda和rainbow饲养了N只小猫,这天,小猫们要去爬山。经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。 Freda和rainbow只好花钱让它们坐索道下山。索道上的缆车最大承重量为W,而N只小猫的重量分别是C1、C2……CN。当然,每辆缆车上的小猫的重量之和不能超过W。每租用一辆缆车,Freda和rainbow就要付1美元,所以他们想知道,最少需要付多少美元才能把这N只小猫都运送下山? |
输入
|
第一行包含两个用空格隔开的整数,N和W。
接下来N行每行一个整数,其中第i+1行的整数表示第i只小猫的重量C i。 |
输出
|
输出一个整数,最少需要多少美元,也就是最少需要多少辆缆车。
|
输入示例
|
5 1996
1 2 1994 12 29 |
输出示例
|
2
|
其他说明
|
数据范围:对于100%的数据,1<=N<=18,1<=C i <=W<=10^8。
|
快看暴力踩标程了!!!
搜索真是一门神奇的学问。
状压DP:
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=18; const int INF=1000000000; int c[maxn],f[1<<maxn],w[1<<maxn],n,W; int main() { n=read();W=read(); rep(i,0,n-1) c[i]=read(); rep(S,0,(1<<n)-1) rep(i,0,n-1) if(S>>i&1) w[S]+=c[i]; f[0]=0; rep(S,1,(1<<n)-1) { f[S]=INF; for(int S0=S;S0;S0=(S0-1)&S) if(w[S0]<=W) f[S]=min(f[S],f[S^S0]+1); } printf("%d ",f[(1<<n)-1]); return 0; }
暴力搜索:
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=18; const int INF=1000000000; int c[maxn],n,w,sum; int A[maxn],d; int dfs(int x) { if(x==n) return 1; rep(i,1,d) if(A[i]+c[x]<=w) { A[i]+=c[x]; if(dfs(x+1)) return 1; A[i]-=c[x]; } return 0; } int main() { n=read();w=read(); rep(i,0,n-1) c[i]=read(),sum+=c[i]; sort(c,c+n); rep(i,0,n-1) if(i<(n-i-1)) swap(c[i],c[n-i-1]); rep(i,sum/w,n) { d=i; if(i==n||dfs(0)) { printf("%d ",i); return 0; } } }
魔兽争霸 |
难度级别:C; 运行时间限制:1000ms; 运行空间限制:262144KB; 代码长度限制:2000000B |
试题描述
|
小x正在销魂地玩魔兽
他正控制着死亡骑士和n个食尸鬼(编号1~n)去打猎死亡骑士有个魔法,叫做“死亡缠绕”,可以给食尸鬼补充HP战斗过程中敌人会对食尸鬼实施攻击,食尸鬼的HP会减少,小x希望随时知道自己部队的情况,即HP值第k多的食尸鬼有多少HP,以便决定如何施放魔法.请同学们帮助他:) 小x向你发出3种信号:(下划线在输入数据中表现为空格) A_i_a表示敌军向第i个食尸鬼发出了攻击,并使第i个食尸鬼损失了a点HP,如果它的HP<=0,那么这个食尸鬼就死了(Undead也是要死的……)。 敌军不会攻击一个已死的食尸鬼。 C_i_a 表示死亡骑士向第i个食尸鬼放出了死亡缠绕,并使其增加了a点HP。 HP值没有上限。 死亡骑士不会向一个已死的食尸鬼发出死亡缠绕 Q_k 表示小x向你发出询问 |
输入
|
第一行,一个正整数 n
以后n个整数 表示n个食尸鬼的初始HP值 接着一个正整数m 以下m行 每行一个小x发出的信号 |
输出
|
对于小x的每个询问,输出HP第k多的食尸鬼有多少HP,如果食尸鬼总数不足k个,输出-1。每个一行数。
最后一行输出一个数:战斗结束后剩余的食尸鬼数 |
输入示例
|
5
1 2 3 4 5 10 Q 2 A 4 6 C 1 4 Q 2 A 2 1 A 3 3 A 1 3 Q 4 C 2 10 Q 1 |
输出示例
|
4
5 -1 11 3 |
其他说明
|
数据范围:
40%的数据 n<=3000 m<=5000 70%的数据 n<=8000 m<=10000 100%的数据 n<=30000 m<=50000 90%的数据随机生成 食尸鬼HP没有上限 数据保证任意时刻食尸鬼的HP值在longint范围内 数据保证A和C命令中的食尸鬼是活着的 输入数据中没有多余空格、换行 |
手写Treap吧。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=30010; struct Node { Node* ch[2]; int r,s,v; void maintain() {s=ch[0]->s+ch[1]->s+1;} }nodes[maxn],*null=&nodes[0]; int ToT;queue<Node*> Q; Node* newnode(int v) { Node* o; if(Q.empty()) o=&nodes[++ToT]; else o=Q.front(),Q.pop(); o->v=v;o->ch[0]=o->ch[1]=null;o->s=1;o->r=rand(); return o; } void del(Node* &o) {Q.push(o);o=null;} void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; o->maintain();k->maintain();o=k; } void insert(Node* &o,int v) { if(o==null) o=newnode(v); else { int d=v>o->v;insert(o->ch[d],v); if(o->ch[d]->r>o->r) rotate(o,d^1); else o->maintain(); } } void remove(Node* &o,int v) { if(o->v==v) { Node* k=o; if(o->ch[0]==null) o=o->ch[1],del(k); else if(o->ch[1]==null) o=o->ch[0],del(k); else { int d=o->ch[0]->r>o->ch[1]->r; rotate(o,d);remove(o->ch[d],v); } } else remove(o->ch[v>o->v],v); if(o!=null) o->maintain(); } int query(Node* &o,int k) { if(k>o->s) return -1; if(o->ch[1]->s+1==k) return o->v; if(o->ch[1]->s>=k) return query(o->ch[1],k); return query(o->ch[0],k-o->ch[1]->s-1); } void print(Node* &o) { if(o==null) return; print(o->ch[0]); printf("%d ",o->v); print(o->ch[1]); } Node *root=null; int n,A[maxn]; int main() { n=read(); rep(i,1,n) insert(root,A[i]=read()); dwn(i,read(),1) { char s[2];int x,k; scanf("%s%d",s,&x); if(s[0]=='Q') printf("%d ",query(root,x)); else if(s[0]=='A') { k=read();remove(root,A[x]); A[x]-=k;if(A[x]>0) insert(root,A[x]); } else { k=read();if(A[x]<=0) continue; remove(root,A[x]); A[x]+=k;insert(root,A[x]); } } printf("%d ",root->s); return 0; }