魔兽争霸
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
char s[maxn];
int main() {
scanf("%s",s);int n=strlen(s);
int T=read();
int x0=,y0=;
rep(i,,n-) {
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,,(T%n)-) {
if(s[i]=='W') x--;
if(s[i]=='E') x++;
if(s[i]=='N') y++;
if(s[i]=='S') y--;
}
printf("%d %d\n",x,y);
return ;
}
西行寺幽幽子
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
typedef long long ll;
struct bign {
int len;
ll c[maxn];
void clean() {
while(len>&&!c[len-]) len--;
}
void operator = (const char* s) {
len=strlen(s);memset(c,,sizeof(c));
rep(i,,len-) c[i]=s[len-i-]-'';
}
void operator *= (const int& b) {
len+=;
rep(i,,len-) c[i]*=b;
rep(i,,len-) c[i+]+=c[i]/,c[i]%=;
clean();
}
bool operator <= (const bign& b) const {
if(len>b.len) return ;
if(len<b.len) return ;
dwn(i,len-,) {
if(c[i]>b.c[i]) return ;
if(c[i]<b.c[i]) return ;
}
return ;
}
void print() {
dwn(i,len-,) 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=,r=;
while(l+<r) {
int mid=l+(r-l>>);
bign t=n;t*=mid;
if(t<=m) l=mid;
else r=mid;
}
printf("%d\n",l);
}
else puts("");
return ;
}
琪露诺
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
typedef long long ll;
const int maxn=;
const int INF=;
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,,n) A[i]=read();
f[]=;
int l=,r=;
rep(i,,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,,n) if(i+R>n&&f[i]>ans) {
ans=f[i];p=i;
}
printf("%d\n",ans);
while(p) S[++top]=p,p=g[p];
printf("");while(top) printf(" %d",S[top--]);
puts(" -1");
return ;
}
上白泽慧音
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
const int maxm=;
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]=<<;
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,,m) {
int u=read(),v=read(),t=read();
AddEdge(u,v);
if(t==) AddEdge(v,u);
}
rep(i,,n) if(!pre[i]) dfs(i);
int ans,mx=,f=;
rep(i,,scc_cnt) if(tot[i]>mx||(tot[i]==mx&&mn[i]<mn[ans])) {
ans=i;mx=tot[i];
}
printf("%d\n",mx);
rep(i,,n) if(num[i]==ans) {
if(!f) f=;
else putchar(' ');
printf("%d",i);
}
return ;
}
环上的游戏
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B
试题描述

有一个取数的游戏。初始时,给出一个环,环上的每条边上都有一个非负整数。这些整数中至少有一个0。然后,将一枚硬币放在环上的一个节点上。两个玩家就是以这个放硬币的节点为起点开始这个游戏,两人轮流取数,取数的规则如下:
(1)选择硬币左边或者右边的一条边,并且边上的数非0;
(2)将这条边上的数减至任意一个非负整数(至少要有所减小);
(3)将硬币移至边的另一端。
如果轮到一个玩家走,这时硬币左右两边的边上的数值都是0,那么这个玩家就输了。
如下图,描述的是Alice和Bob两人的对弈过程,其中黑色节点表示硬币所在节点。结果图(d)中,轮到Bob走时,硬币两边的边上都是0,所以Alcie获胜。

现在,你的任务就是根据给出的环、边上的数值以及起点(硬币所在位置),判断先走方是否有必胜的策略。

输入
第一行一个整数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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
int A[],n;
int main() {
n=read();
rep(i,,n) A[i]=read();
int r=n,l=;
while(A[r]) r--;
while(A[l+]) l++;
r=n-r;
if(l&||r&) puts("YES");
else puts("NO");
return ;
}
舞蹈课
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
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=;
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+);
rep(i,,n) {
Left[i]=i-;Right[i]=i+;
A[i]=read();type[i]=s[i]=='G';
}
rep(i,,n-) if(type[i]!=type[i+]) Q.push((Pair){i,abs(A[i]-A[i+])});
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\n",pos,x.v);
del[pos]=del[Right[pos]]=;
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\n",top);
rep(i,,top) printf("%d %d\n",ans1[i],ans2[i]);
return ;
}
栅栏迷宫
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
const int mx[]={,-,,};
const int my[]={,,,-};
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()<<|,n=read()<<|;
rep(i,,n) cin.getline(A[i]+,);
rep(i,,n) rep(j,,m) if(i==||j==||i==n||j==m) {
if(A[i][j]==' ') {
int x=i,y=j;if(i==) x++;if(i==n) x--;
if(j==) y++;if(j==m) y--;
vis[x][y]=;d[x][y]=;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,,) {
int nx=x+mx[dir],ny=y+my[dir];
if(nx+mx[dir]>=&&nx+mx[dir]<=n&&ny+my[dir]>=&&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]+;
vis[nx][ny]=;
Q.push((Point){nx,ny});
}
}
}
printf("%d\n",ans);
return ;
}
/*
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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
typedef long long ll;
const int maxn=;
const int maxc=<<;
const int HASH=;
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=;
ren if(val[i]==p) ans=min(ans,val2[i]);
return ans;
}
int n,m,ans;
int main() {
n=read();m=read();ans=n+;
rep(i,,n-) {
M[i]=1ll<<i;
if(read()) now|=1ll<<i;
}
rep(i,,m) {
int x=read()-,y=read()-;
M[x]|=1ll<<y;M[y]|=1ll<<x;
}
int n0=n/,n1=n-n0;
rep(S,,(<<n0)-) {
ll t=;int cnt=;
rep(i,,n0-) if(S>>i&) t^=M[i],cnt++;
insert(t,cnt);
}
rep(S,,(<<n1)-) {
ll t=;int cnt=;
rep(i,,n1-) if(S>>i&) t^=M[i+n0],cnt++;
ans=min(ans,cnt+query(((1ll<<n)-)^t^now));
}
printf("%d\n",ans);
return ;
}
某种密码
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
const int maxc=<<;
const int HASH=;
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]=;next[cnt]=first[pos];first[pos]=cnt;
}
int query(int p) {
int pos=p%HASH;
ren if(val[i]==p) return tot[i];
return ;
}
int main() {
n=read();m=read();long long ans=;
rep(i,,n-) A[i]=read();
int n0=n/,n1=n-n0;
rep(S,,(<<n0)-) {
int t=;
rep(i,,n0-) if(S>>i&) t+=A[i];
insert(t);
}
rep(S,,(<<n1)-) {
int t=;
rep(i,,n1-) if(S>>i&) t+=A[i+n0];
ans+=query(m-t);
}
printf("%lld\n",ans);
return ;
}
球的序列
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
const int INF=;
int n,ans,A[maxn],C[maxn],g[maxn];
int main() {
n=read();
rep(i,,n) A[read()]=i;
rep(i,,n) C[i]=A[read()],g[i]=INF;
rep(i,,n) {
int k=lower_bound(g+,g+n+,C[i])-g;
ans=max(k,ans);g[k]=C[i];
}
printf("%d\n",ans);
return ;
}
大逃亡
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
const int mx[]={,-,,};
const int my[]={,,,-};
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,,) {
int nx=x+mx[dir],ny=y+my[dir];
if(nx>=&&nx<n&&ny>=&&ny<m&&!vis[nx][ny]) {
vis[nx][ny]=;T[nx][ny]=T[x][y]+;
Q.push(nx*m+ny);
}
}
}
memset(vis,,sizeof(vis));
}
int check(int mid,int x1,int x2,int y1,int y2) {
if(T[x1][y1]<mid) return ;
memset(vis,,sizeof(vis));
Q.push(x1*m+y1);vis[x1][y1]=;d[x1][y1]=;
while(!Q.empty()) {
int x=Q.front()/m,y=Q.front()%m;Q.pop();
rep(dir,,) {
int nx=x+mx[dir],ny=y+my[dir];
if(nx>=&&nx<n&&ny>=&&ny<m&&!vis[nx][ny]&&T[nx][ny]>=mid) {
vis[nx][ny]=;d[nx][ny]=d[x][y]+;
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,,c) {
int x=read(),y=read();
T[x][y]=;vis[x][y]=;Q.push(x*m+y);
}
caltime();
int l=,r=max(n,m)+,mid;
while(l+<r) if(check(mid=l+r>>,x1,x2,y1,y2)) l=mid; else r=mid;
check(l,x1,x2,y1,y2);printf("%d %d\n",l,d[x2][y2]);
return ;
}
祖孙询问
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
int first[maxn],next[maxn<<],to[maxn<<],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,,n) {
int x=read(),y=read();
if(y<) rt=x;
else AddEdge(x,y);
}
dfs(rt,);
int q=read();
while(q--) {
int x=read(),y=read();
if(L[x]<=L[y]&&L[y]<=R[x]) puts("");
else if(L[y]<=L[x]&&L[x]<=R[y]) puts("");
else puts("");
}
return ;
}
比赛
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
long double A[maxn],B[maxn],ans;
int main() {
int n=read();
double sum=,sum2=,all=,all2=;
rep(i,,n) A[i]=read();
rep(i,,n) B[i]=read(),all-=*B[i],all2+=B[i]*B[i];
sort(A+,A+n+);sort(B+,B+n+);
int cur=;
rep(i,,n) {
while(cur<n&&B[cur+]<=A[i]) cur++,sum-=*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\n",ans2);
return ;
}
数字
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
typedef long long ll;
const int maxn=;
const int mod=;
int n,ok[],f[maxn][maxn*];
char s[];
int main() {
n=read();scanf("%s",s);
dwn(i,strlen(s)-,) ok[s[i]-'']=;
f[][]=;
rep(i,,n) rep(j,,i*) {
int& ans=f[i][j];
dwn(k,min(,j),) if(ok[k]) ans+=f[i-][j-k];
ans%=mod;
}
ll ans=,ans2=,ans3=;
rep(j,,n*) (ans+=(ll)f[n][j]*f[n][j])%=mod;
ans=ans*%mod;
rep(i,,n*) (ans2+=(ll)f[n/][i]*f[n/][i])%=mod;
rep(i,,n*) {
if(!(n&)) (ans3+=(ll)f[n/][i]*f[n/][i])%=mod;
else (ans3+=(ll)f[n/+][i]*f[n/+][i])%=mod;
}
printf("%lld\n",((ans-ans2*ans3)%mod+mod)%mod);
return ;
}
锻炼计划
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
typedef long long ll;
const int maxn=;
ll A[maxn];
char s[maxn];
int main() {
ll n=read();
dwn(i,read(),) {
scanf("%s",s);
int s=read(),t=read(),v=read();
rep(j,s,t) A[j]+=v;
}
rep(i,,) {
n++;n-=A[i];
if(n<=) {printf("Runtime Error\n%d\n",i);return ;}
}
printf("Accepted\n%lld\n",n);
return ;
}
小猫爬山
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
const int INF=;
int c[maxn],f[<<maxn],w[<<maxn],n,W;
int main() {
n=read();W=read();
rep(i,,n-) c[i]=read();
rep(S,,(<<n)-) rep(i,,n-) if(S>>i&) w[S]+=c[i];
f[]=;
rep(S,,(<<n)-) {
f[S]=INF;
for(int S0=S;S0;S0=(S0-)&S) if(w[S0]<=W) f[S]=min(f[S],f[S^S0]+);
}
printf("%d\n",f[(<<n)-]);
return ;
}

暴力搜索:

#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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
const int INF=;
int c[maxn],n,w,sum;
int A[maxn],d;
int dfs(int x) {
if(x==n) return ;
rep(i,,d) if(A[i]+c[x]<=w) {
A[i]+=c[x];
if(dfs(x+)) return ;
A[i]-=c[x];
}
return ;
}
int main() {
n=read();w=read();
rep(i,,n-) c[i]=read(),sum+=c[i];
sort(c,c+n);
rep(i,,n-) if(i<(n-i-)) swap(c[i],c[n-i-]);
rep(i,sum/w,n) {
d=i;
if(i==n||dfs()) {
printf("%d\n",i);
return ;
}
}
}
魔兽争霸
难度级别: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=,f=;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
return x*f;
}
const int maxn=;
struct Node {
Node* ch[];
int r,s,v;
void maintain() {s=ch[]->s+ch[]->s+;}
}nodes[maxn],*null=&nodes[];
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[]=o->ch[]=null;o->s=;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^];o->ch[d^]=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^);
else o->maintain();
}
}
void remove(Node* &o,int v) {
if(o->v==v) {
Node* k=o;
if(o->ch[]==null) o=o->ch[],del(k);
else if(o->ch[]==null) o=o->ch[],del(k);
else {
int d=o->ch[]->r>o->ch[]->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 -;
if(o->ch[]->s+==k) return o->v;
if(o->ch[]->s>=k) return query(o->ch[],k);
return query(o->ch[],k-o->ch[]->s-);
}
void print(Node* &o) {
if(o==null) return;
print(o->ch[]);
printf("%d ",o->v);
print(o->ch[]);
}
Node *root=null;
int n,A[maxn];
int main() {
n=read();
rep(i,,n) insert(root,A[i]=read());
dwn(i,read(),) {
char s[];int x,k;
scanf("%s%d",s,&x);
if(s[]=='Q') printf("%d\n",query(root,x));
else if(s[]=='A') {
k=read();remove(root,A[x]);
A[x]-=k;if(A[x]>) insert(root,A[x]);
}
else {
k=read();if(A[x]<=) continue;
remove(root,A[x]);
A[x]+=k;insert(root,A[x]);
}
}
printf("%d\n",root->s);
return ;
}

最新文章

  1. input只能输入数字并限制长度
  2. 关于iBatis.NET连接各数据库时提示没找到数据库驱动的依赖文件
  3. org.dom4j.DocumentException : 1 字节的 UTF-8 序列的字节 1 无效。 Nested exception: 1 字节的 UTF-8 序列的字节 1 无效。
  4. 团队项目--站立会议 DAY4
  5. 【转载】错误:ORA-28002: the password will expire within 7 days 解决方法
  6. iOS消息推送机制的实现
  7. [Python][MachineLeaning]Python Scikit-learn学习笔记1-Datasets&amp;Estimators
  8. 产生冠军(set,map,拓扑结构三种方法)
  9. 关于jq操作table下多个type=radio的input的选中
  10. 【死磕Java并发】-----Java内存模型之happens-before
  11. java final、finally、finalize
  12. Codeforces 785 D. Anton and School - 2
  13. Xshell中文乱码怎么处理?
  14. 结合以太通道的VLAN配置
  15. PHP Jquery 代码操作 内容 属性 样式 事件 Json数据
  16. webpack+express多页站点开发
  17. Spring Boot + MyBatis + Pagehelper 配置多数据源
  18. vue项目 axios封装第二弹
  19. mof提权原理及实现
  20. VUE+WebPack游戏设计:&#39;乘法防线&#39;游戏设计

热门文章

  1. 解决pdf打印预览中遇到特殊字符,导出失败问题
  2. matplotlib交互模式与pacharm单独Figure设置
  3. 20155206 2016-2017-2 《Java程序设计》第7周学习总结
  4. 【转】用CornerStone配置SVN,HTTP及svn简单使用说明
  5. shell 判断为空打印
  6. 开放通用Api,总有你喜欢的
  7. 对linux内核中jiffies+Hz表示一秒钟的理解
  8. 利用shell找出15分钟内修改的文件
  9. how-to-pass-a-class-variable-to-a-decorator-inside-class-definition
  10. 关于spring中Assert的应用(方法入参检测工具类)