zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",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
    ",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
    ",pos,x.v);
            del[pos]=del[Right[pos]]=1;
            ans1[++top]=pos;ans2[top]=Right[pos];
            if(Left[pos]&&Right[Right[pos]]<=n) 
                if(type[Left[pos]]!=type[Right[Right[pos]]])
                   Q.push((Pair){Left[pos],abs(A[Left[pos]]-A[Right[Right[pos]]])});
            Left[Right[Right[pos]]]=Left[pos];
            Right[Left[pos]]=Right[Right[pos]];
        }
        printf("%d
    ",top);
        rep(i,1,top) printf("%d %d
    ",ans1[i],ans2[i]);
        return 0;
    }
    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
    ",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
    ",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
    ",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
    ",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
    ",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
    ",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
    ",((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
    %d
    ",i);return 0;}
        }
        printf("Accepted
    %lld
    ",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
    ",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
    ",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
    ",query(root,x));
             else if(s[0]=='A') {
                 k=read();remove(root,A[x]);
                 A[x]-=k;if(A[x]>0) insert(root,A[x]);     
             }
             else {
                 k=read();if(A[x]<=0) continue;
                 remove(root,A[x]);
                 A[x]+=k;insert(root,A[x]);     
             }             
        }
        printf("%d
    ",root->s);
        return 0;    
    }
    View Code
  • 相关阅读:
    爬虫必看,每日JS逆向之爱奇艺密码加密,今天你练了吗?
    每日JS逆向练习之斗鱼登录密码加密,今天你练了吗?
    兄弟们,我打算抠100个网站JS加密代码召唤,一个也跑不掉,这次轮到小虎牙
    这个爬虫JS逆向加密任务,你还不来试试?逆向入门级,适合一定爬虫基础的人
    兄弟,你爬虫基础这么好,需要研究js逆向了,一起吧(有完整JS代码)
    四十一:快速排序(递归)
    第四十次发博不知道用什么标题好
    第三十九次发博不知道发什么好
    第三十八次发博不知道用什么标题好
    第三十七次发博不知道用什么标题好
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/4936368.html
Copyright © 2011-2022 走看看