2019暑假集训DAY1(problem3.play)(单调栈思想)

题面

play

题目大意

这个位面存在编号为1~2N的2N个斗士,他们正为争夺斗士大餐展开R轮PVP,每个斗士i都有一个固有APM ai,和一个初始斗士大餐储量 bi。每轮开始前以及最后一轮结束之后,
2N个斗士会重新按照各自斗士大餐的储量进行排序(斗士大餐储量相同时编号小的靠前),每轮中,第1名和第2名PVP,第3名和第4名PVP,……第2k-1名和第2k名PVP,第2N-1名和第2N名PVP。
而每场一对一的PVP都非常无聊,总是两个斗士中APM高的获胜,另一方失败;或者APM相同的两方取得平手。每轮赛后获胜方获得2份斗士大餐,平均双方均获得1份斗士大餐。
求输出最后排名从小到大的各斗士编号。

输入格式

第一行两个整数N、R;
第二行2N个整数,bi;
第三行2N个整数, ai;

输出格式

2N个整数,R轮比赛后排名从小到大的各斗士编号;

样例输入

10 10
0 10 49 24 7 1 64 8 52 81 4 9 40 17 52 17 40 0 97 77
0 1 0 1 1 1 0 2 1 0 0 2 1 1 2 0 1 1 1 0

样例输出

19 10 20 7 15 9 13 17 3 4 14 12 8 2 16 5 6 18 11 1

数据范围

10%的数据:N≤10,R≤10,ai≤1e8,bi≤1e8
30%的数据:N≤1e2,R≤60,ai≤1e8,bi≤1e8
70%的数据:N≤1e4,R≤60,ai≤1e8,bi≤1e8
100%的数据:N≤1e5,R≤60,ai≤1e8,bi≤1e8



一开始看到这道题,确实是没有什么思路的(可能是上午的课真的还听的蛮模糊的),就只打了个暴力单纯模拟,数据很水,没想到还能得70分

考试时的代码

#include<bits/stdc++.h>
#define N 200003
using namespace std;
int read()
{
    int f=1,x=0;char s=getchar();
    while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar();}
    while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();}
    return x*f;
}
struct dou{
    int a,b,ord;
}w[N];
bool cmp(const dou &x,const dou &y)
{
    if(x.b==y.b)return x.ord<y.ord;
    return x.b>y.b;
}
int main()
{
    freopen("play.in","r",stdin);
    freopen("play.out","w",stdout);
    int n=read(),r=read();
    n=n*2;
    for(int i=1;i<=n;++i)
    {
        w[i].b=read();w[i].ord=i;
    }
    for(int i=1;i<=n;++i)
      w[i].a=read();
    while(r--)
    {
        sort(w+1,w+1+n,cmp);
        for(int i=1;i<=n;i+=2)
        {
            if(w[i].a>w[i+1].a)w[i].b+=2;
            else if(w[i].a<w[i+1].a)w[i+1].b+=2;
            else w[i].b++,w[i+1].b++;
        }
    }
    sort(w+1,w+1+n,cmp);
    for(int i=1;i<=n;++i)
      printf("%d ",w[i].ord);
}
/*

*/

70分

每两个比较完后,就更新。

其实时间就费在每一次都要将序列重新排序,每一次都是O(nlogn)的,于是轻轻松松炸掉。



那么正解的思路是什么呢?

再仔细分析题目,会发现每一次更新后每一个数可分为这样三种情况:

1.值+2

2.值不变

3.值加一

因为每一次操作前都是排好序的,(以第一种为例)那么在同一种情况中,前面值+2的肯定比后面值也要+2的大,毕竟原来排序就在前面,都加了2后,在前面的还是在前面。

那么就发现,对于每一种情况都是满足单调性的。

于是我们可以开三个数组A,B,C,分别存这三种情况,再以归并排序的思想去合并,就可以在差不多O(2*n)(其实就可以看作是O(n))的时间里完成一个在这一次操作后从大到小的序列,而不是用O(nlogn)的时间来快排。R也不大,于是这可以A掉这道题了。

代码很简短,也很好理解。

#include<bits/stdc++.h>
#define N 200003
using namespace std;
int read()
{
    int f=1,x=0;char s=getchar();
    while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar();}
    while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();}
    return x*f;
}
struct dou{
    int a,b,ord;
}w[N],A[N],B[N],C[N];
bool cmp(const dou &x,const dou &y)
{
    if(x.b==y.b)return x.ord<y.ord;
    return x.b>y.b;
}
int cnta=0,cntb=0,cntc=0;
int main()
{
    freopen("play.in","r",stdin);
    freopen("play.out","w",stdout);
    int n=read(),r=read();
    n=n*2;
    for(int i=1;i<=n;++i)
    {
        w[i].b=read();w[i].ord=i;
    }
    for(int i=1;i<=n;++i)
      w[i].a=read();
    sort(w+1,w+1+n,cmp);
    while(r--)
    {
        cnta=0;cntb=0;cntc=0;//记得清零,每一次都以相同的步骤来更新序列保持b从大到小
        for(int i=1;i<=n;i+=2)//分别存入三个数组中,保持单调性
        {
            if(w[i].a>w[i+1].a)
            {
                w[i].b+=2;
                A[++cnta]=w[i];
                B[++cntb]=w[i+1];
            }
            else if(w[i].a<w[i+1].a)
            {
                w[i+1].b+=2;
                A[++cnta]=w[i+1];
                B[++cntb]=w[i];
            }
            else
            {
                w[i].b++;w[i+1].b++;
                C[++cntc]=w[i];
                C[++cntc]=w[i+1];
            }
        }
        int s1=1,s2=1,sum=1;
    //    printf("!!%d %d %d\n",cnta,cntb,cntc);
        while(s1<=cnta&&s2<=cntb)//归并排序的思想
        {
            if(A[s1].b>B[s2].b||(A[s1].b==B[s2].b&&A[s1].ord<B[s2].ord)) w[sum++]=A[s1++];
            else if(A[s1].b<B[s2].b||(A[s1].b==B[s2].b&&A[s1].ord>B[s2].ord)) w[sum++]=B[s2++];
        }
        while(s1<=cnta) w[sum++]=A[s1++];
        while(s2<=cntb) w[sum++]=B[s2++];
        for(int i=1;i<=cnta+cntb;++i) A[i]=w[i];//因为有三个数组,所以这是必要的。先把AB合并后,再以相同的思想合并C,保证正确性。
        s1=1;s2=1;sum=1;//或者说,直接用三个指针来比较?但代码没有这样容易实现呢(因为还有一个b相同比较ord的条件)(反正这样写时间复杂度也是保证了的)
        while(s1<=cnta+cntb&&s2<=cntc)
        {
            if(A[s1].b>C[s2].b||(A[s1].b==C[s2].b&&A[s1].ord<C[s2].ord)) w[sum++]=A[s1++];
            else  w[sum++]=C[s2++];
        }
        while(s1<=cnta+cntb)w[sum++]=A[s1++];
        while(s2<=cntc)w[sum++]=C[s2++];
    }
    for(int i=1;i<=n;++i)
      printf("%d ",w[i].ord);
}
/*

*/

单调性

好吧,其实这道题和今天上午讲的NOIP 2016 D2T2 蚯蚓 有相同的思想。

在不考虑q的时候,其实就可以把每一次切断后的较长蚯蚓放入一个单调的数组A,较短蚯蚓放入一个单调数组B,同样的,这也是满足单调性的(因为q是一定的)。

然后每一次要切蚯蚓的时候,就是取出A,B,还有原序列(还没被I切过的那些蚯蚓)的“队首”(这是必要的,因为会存在原序列的“队首”小于A或B的“队首”,前面切过的某条实在是太长了)。

那q怎么办?(借鉴洛谷题解)

其实最麻烦的就是每一其他蚯蚓的长度要增加q,直接暴力的话就很费时间。

我们可以省去每一秒增加每只蚯蚓的长度这个操作,转换成在查询砍那只蚯蚓时,把增加的长度算到蚯蚓的总长度上。

emmm,只是理解了思想,具体还要看实现(记得填坑)。

原文地址:https://www.cnblogs.com/yyys-/p/11160681.html

时间: 2024-11-09 08:41:06

2019暑假集训DAY1(problem3.play)(单调栈思想)的相关文章

2019南昌网络赛-I(单调栈+线段树)

题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上弄了3小时,思路大体都是对的,但就是没法实现.这几天恶补线段树. 首先可以利用单调栈来查找满足a[i]为最小值的最大区间L[i]~R[i].然后利用线段树求一个段的和sum.最小前缀lsum和最小后缀rsum.然后遍历a[i]: a[i]>0:最优为sum(L[i],R[i])*a[i] a[i]<

暑假D9 T2 extra(单调栈优化DP)

题意 一条路被划分成了n段,每一段有一个高度,一个人有步幅k,代表他最多可以从第x段一步到第x+k段,当h[x]>h[x+k]时,不消耗体力,否则消耗一点体力,求最后到第n段路时最少消耗的体力,最初在第一段路. 对于 30%的数据,保证 T = 1:对于另 20% 的数据,保证 N <= 500:对于 100% 的数据,保证 n< = 1e6, T <= 10,ai在 int 内,0 < K <n. 题解 很容易想到转移方程 f[i]= h[i]<h[j] ? f

「总结」2019暑假集训

啊,我最喜欢的暑假集训终究还是结束了. 感觉集训收获的还是挺大的,不管是在知识方面还是心态方面,感觉现在考试心态稳了很多,不管是考前考时考后,都可以很快的调整了.大概就是教练所说的考试心态调整的加速.最近感觉非常好,虽然水题还是老爆零,考得也不怎么样,不过我的确是飞快的在进步了,只要我在进步就好了,我很心满意足的. 总结一下接近30场的考试吧. 一开始的7场一直只有50分左右,而且还有两次没有交卷子,气得我把纸贴在电脑上提醒自己交卷子,虽然成绩并不怎么样,不过我还好在也不犯这个错误了. 然后就理

暑假集训day1

其实这是前天的事了.(现在时间回到两天前) 今天的主要内容是最短路和2-SAT 最短路我做了一题:水灾:题目详情见9018-1452 先bfs求出洪水漫延到每一个点的时间. 然后再跑一遍bfs求出最短路即可. #include<iostream> #include<cstdio> #include<cstring> using namespace std; int INF=0x7fffffff; int n,m,sx,sy,dx,dy,h=0,now=0; int t[

【2019寒假集训DAY1】背包问题

基本信息 金矿模型理解:http://www.cnblogs.com/sdjl/articles/1274312.html 适用条件: 重叠子结构.最优子问题.边界.子问题独立+转移方程式 背包九讲(结合题目):http://www.cnblogs.com/jbelial/articles/2116074.html 框架 01背包问题: 无优化 for(int i=1;i<=n;i++) { for(int c=0;c<=m;c++) { f[i][c]=f[i-1][c]; if(c>

2019暑假集训 Intervals

题目描述 给定n个闭区间[ai,bi]和n个整数ci.你需要构造一个整数集合Z,使得对于任意i,Z中满足ai<=x<=bi的x不少于ci个.求Z集合中包含的元素个数的最小值. 输入 第一行为一个整数n(1<=n<=50000) 接下来n行每行描述一个区间,三个整数分别表示ai,bi和ci.( 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1.) 输出 输出一个整数,表示Z中包含元素个数的最小值. 样例输入 5

2019暑假集训 8/2

学习内容:线段树+可持久化线段树 今日完成题数(不包含多校):5 /*多校补题情况(之前定的每支队伍标准):?*/ 今日看书情况:3页 学习算法的总结 可持久化线段树一直没有好好研究 直到最近着重开始写线段树专题 写了一些权值线段树 才把之前有的疑问的解决了 今日做题总结    hdu-6183 https://www.cnblogs.com/MengX/p/11291321.html hdu-4630 https://www.cnblogs.com/MengX/p/11291349.html

2019暑假集训 数字游戏

题目描述 科协里最近很流行数字游戏.某人命名了一种不降数,这种数字必须满足从左到右各位数字成小于等于的关系,如123,446.现在大家决定玩一个游戏,指定一个整数闭区间[a,b],问这个区间内有多少个不降数. 输入 有多组测试数据.每组只含两个数字a,b,意义如题目描述. 输出 每行给出一个测试数据的答案,即[a,b]之间有多少不降数. 样例输入 1 9 1 19 样例输出 9 18 提示 对于全部数据,1<=a<=b<=2^31-1. 裸的数位dp,每一位从上一位开始搜即可(保证不下降

2019暑假集训 8/16

学习内容:网络流 今日完成题数(不包含多校): 今日看书情况:无 学习算法的总结 有上下界的网络流 和 最小费用可行流 今日做题总结 总结 今日心得: https://blog.csdn.net/forever_dreams/article/details/81879277 https://blog.csdn.net/qq_43202683/article/details/90049597 明日任务:   明天写完基础的可行流题目 就可以写一写gym的进阶题 原文地址:https://www.c