NOIP练习赛题目3

魔兽争霸
难度级别: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\n",x,y);
    return 0;
}
View Code

 

西行寺幽幽子
难度级别: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\n",l);
    }
    else puts("0");
    return 0;
}
View Code

 

琪露诺
难度级别: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\n",ans);
    while(p) S[++top]=p,p=g[p];
    printf("0");while(top) printf(" %d",S[top--]);
    puts(" -1");
    return 0;
}
View Code

 

上白泽慧音
难度级别: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\n",mx);
    rep(i,1,n) if(num[i]==ans) {
        if(!f) f=1;
        else putchar(' ');
        printf("%d",i);
    }
    return 0;
}
View Code

 

环上的游戏
难度级别: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=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;
}
View Code

 

 

舞蹈课
难度级别: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\n",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\n",top);
    rep(i,1,top) printf("%d %d\n",ans1[i],ans2[i]);
    return 0;
}
View Code

 

栅栏迷宫
难度级别: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\n",ans);
    return 0;
}
/*
5 3
+-+-+-+-+-+
|         |
+-+ +-+ + +
|     | | |
+ +-+-+ + +
| |     |  
+-+ +-+-+-+
*/
View Code

 

人偶师
难度级别: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\n",ans);
    return 0;
}
View Code

 

某种密码
难度级别: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\n",ans);
    return 0;
}
View Code

 

球的序列
难度级别: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\n",ans);
    return 0;
}
View Code

 

大逃亡
难度级别: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\n",l,d[x2][y2]);
    return 0;
}
View Code

 

祖孙询问
难度级别: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;
}
View Code

 

比赛
难度级别: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\n",ans2);
    return 0;
}
View Code

 

数字
难度级别: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\n",((ans-ans2*ans3)%mod+mod)%mod);
    return 0;
}
View Code

 

锻炼计划
难度级别: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\n%d\n",i);return 0;}
    }
    printf("Accepted\n%lld\n",n);
    return 0;
}
View Code

 

小猫爬山
难度级别: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\n",f[(1<<n)-1]);
    return 0;
}
View Code

暴力搜索:

#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\n",i);
            return 0;
        }
    }
}
View Code

 

 

魔兽争霸
难度级别: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\n",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\n",root->s);
    return 0;    
}
View Code

 

posted @ 2015-11-04 16:21  wzj_is_a_juruo  阅读(565)  评论(0编辑  收藏  举报