魔兽争霸
难度级别: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 ;
}

NOIP练习赛题目3的更多相关文章

  1. NOIP练习赛题目6

    长途旅行 难度级别:A: 运行时间限制:3000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 JY 是一个爱旅游的探险家,也是一名强迫症患者.现在JY 想要在C ...

  2. NOIP练习赛题目5

    小象涂色 难度级别:C: 运行时间限制:1000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 小象喜欢为箱子涂色.小象现在有c种颜色,编号为0~c-1:还有n个箱 ...

  3. NOIP练习赛题目1

    有些题目可能没做,如计算几何.恶心模拟. 高级打字机 难度级别:C: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 早苗入手了最新的高级打字机 ...

  4. NOIP练习赛题目4

    肥得更高 难度级别:C: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 自2009年以来,A.B站的历史就已经步入了农业变革的黎明期.在两站的 ...

  5. NOIP练习赛题目2

    小K的农场 难度级别:C: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个 ...

  6. 【NOIP练习赛】开车

    [NOIP练习赛]T2.开车 Description 老司机小 Q 要在一个十字路口指挥车队开车,这个十字路口可 以描述为一个 n*n 的矩阵,其中第 2 行到第 n-1 行都各有一道横向车 道,第 ...

  7. Noip往年题目整理

    Noip往年题目整理 张炳琪 一.历年题目 按时间倒序排序 年份 T1知识点 T2知识点 T3知识点 得分 总体 2016day1 模拟 Lca,树上差分 期望dp 144 挺难的一套题目,偏思维难度 ...

  8. 【NOIP练习赛】学习

    [NOIP练习赛]T3.学习 Description 巨弱小 D 准备学习,有 n 份学习资料给他看,每份学习资料的 内容可以用一个正整数 ai 表示.小 D 如果在一天内学习了多份资料, 他只能记住 ...

  9. NOIp初赛题目整理

    NOIp初赛题目整理 这个 blog 用来整理扶苏准备第一轮 csp 时所做的与 csp 没 有 关 系 的历年 noip-J/S 初赛题目,记录了一些我从不知道的细碎知识点,还有一些憨憨题目,不定期 ...

随机推荐

  1. linux服务器安装mysql并配置外网访问

    linux服务器安装mysql并配置外网访问 更新系统,如果不运行该命令,直接安装mysql,会出现"有几个软件包无法下载 sudo apt-get update 安装mysql sudo ...

  2. bzoj千题计划290:bzoj3143: [Hnoi2013]游走

    http://www.lydsy.com/JudgeOnline/problem.php?id=3143 计算每条边经过的概率e[] 然后经过概率多的分配的编号大,经过概率少的分配的编号小 如何计算边 ...

  3. [转] Android 性能分析案例

    Android 系统的一个工程师(Romain Guy)针对Falcon Pro  应用,撰写了一个Android性能分析的文章.该文章介绍了如何分析一个应用哪里出现了性能瓶颈,导致该应用使用起来不流 ...

  4. CSS-3 渐变的使用

    CSS3 渐变(gradients)可以让您在两个或多个指定的颜色之间显示平稳的过渡. 以前,您必须使用图像来实现这些效果.但是,通过使用 CSS3 渐变(gradients),您可以减少下载的事件和 ...

  5. CS229 笔记06

    CS229 笔记06 朴素贝叶斯 事件模型 事件模型与普通的朴素贝叶斯算法不同的是,在事件模型中,假设文本词典一共有 \(k\) 个词,训练集一共有 \(m\) 封邮件,第 \(i\) 封邮件的词的个 ...

  6. shell 判断文件出现次数

    判断 file 文件中 第一个变量 出现次数 awk '{print $1}' file |sort |uniq -c|sort -k1r

  7. linux内核环形缓冲区【转】

    转自:https://blog.csdn.net/eydwyz/article/details/56671023 循环缓冲区在一些竞争问题上提供了一种免锁的机制,免锁的前提是,生产者和消费 都只有一个 ...

  8. bootstrap 引用注意事项

    2014年6月8日 13:35:31 bootstrap 提供了cdn服务,在引用css,js的时候注意先后顺序,firebug就不会报错 <script src="http://cd ...

  9. Android 5.0 行为变更

    Android 5.0 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更.本文重点介绍您应该了解并在开发应用时加以考虑的一些主要变更. 如果您之前发布过 Android 应用,请注意 ...

  10. 用原生js实现ajax

    // 通过createXHR()函数创建一个XHR对象 function createXHR() { if(window.XMLHttpRequest) { // IE7.Firefox.Opera. ...