2019.8.10 NOIP模拟测试16 反思总结【基本更新完毕忽视咕咕咕】

一如既往先放代码,我还没开始改…


改完T1滚过来了,先把T1T2的题解写了【颓博客啊】

今天下午就要走了,没想到还有送行的饯别礼,真是欣喜万分【并没有】

早上刚码完前面的总结,带着不怎么有希望的心情开始考试,居然一反前两次考试的没精神+早早做完题开始摸鱼,这次想了很久码了很久一直紧张到最后一刻

挺开心的,虽然还是考得不太好,但好歹T2是为数不多考场AC的人【虽然讲过…】,并且抢了第一个AC

T1我打得挺麻烦的,居然也没崩0分,大约已经有进步了…只是T3实在没时间了很遗憾

于是我成为了得分结构最奇怪的人发扬某人的精神

T1Blue:

来看看什么叫数据结构学傻+网络流学傻

最近被网络流搞到自闭,导致今天打开题面的第一眼:诶,这不是网络流,和蜥蜴那个题好像

一看数据范围,当场暴毙。

但是我的思维已经被网络流荼毒了,导致我的后续措施是:点向一个范围连边?好,来,线段树优化建图

然后我居然真的打了线段树优化建图+最大流…怎么说呢我能写出来还没出锅拿了三十分证明我数据结构真的学傻我还是挺佩服自己的…

正解哪有那么麻烦,如果我不会网络流我八成就A了【你醒醒】

贪心,考虑每只青蛙的起点终点以及能跳的距离全都是一样的,那么对于前面的青蛙,它尽量往远跳,一定会放宽后面青蛙落脚点的限制。并且每只青蛙的贡献都是相同的,所以贡献能拿就拿。

于是石块位置全都丢进set里,每一次upper_bound-1找对于当前位置的最远落脚点,一直往后跳直到能过河,每到一个点删一个点。然后再跳下一只,set空了或者答案满了就break。

然后还要记得特判,d>l的时候一定全能过河。我这个地方特判居然写成了return 0…

#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
int T;
int n,ans,m,d,l;
set<int>s;
set<int>::iterator it;
int main()
{
//    freopen("1.in","r",stdin);
//    freopen("1.out","w",stdout);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%d",&n,&m,&d,&l);
        s.clear();
        ans=0;
        for(int i=1,x;i<=n;i++){
            scanf("%d",&x);
            s.insert(x);
        }
        if(d>=l){
            printf("Excited
");
            continue;
        }
        for(int i=1;i<=n;i++){
            if(s.empty())break;
            int pos=0;
            while(pos+d<l){
                if(s.empty())break;
                it=s.upper_bound(pos+d);
                if(it==s.begin())break;
                it--;
                pos=*it;
                s.erase(it);
                if(pos+d>=l){
                    ans++;
                    break;
                }
            }
            if(ans==m)break;
        }
        if(ans==m){
            printf("Excited
");
        }
        else printf("%d
",ans);
    }
    return 0;
}
T1

【稍微记一下迭代器和*取值吧…】

【我居然没背过,再记一下set<int>::iterator it这个式子】

T2Weed:

看完第一题题面瞬间决定线段树优化建图+网络流以后,我本着先过一眼三道题题面的习惯,先来看第二题。

诶?金坷垃?小麦亩产一千八

不是,这题好生眼熟!因为我一贯都在前排摸鱼认真听课,我认出这是一道不可多得的讲过的题

但是怎么做呢,分块,莫队?仔细纠结了一会儿感觉m个操作可以用线段树来搞,于是到这里好像要下放什么标记的模糊记忆苏醒了,笃定了是线段树。

不过这道题讲完以后好像再也没有人做也没人想起了,于是我的印象只留下了要打好几个标记,下放的时候要递归…其实主要思路已经出来一小半了。

但是因为我和正解的绝缘性以及当时这题似乎很麻烦,我还是决定打暴力。敲完第一题过来摸第二题的暴力,发现并不会写…【其实很显然就是开一个栈】

于是开始凭着不多的记忆推正解,先试着打了铺了多少层和要删多少层的标记,试图写update下放更新的时候发现处理得有点混乱。先按这一块里有铺了多少删了多少一通瞎搞,发现原始m个操作都没处理好…并且其实v的意义以及最后要输出什么都没理解清楚。

铺会给一个v的高度,而删会直接删掉v层,最后求的是剩下的高度…

好吧,重新理解,然后瞎搞。小样例第一个操作的输出就不对。手玩一下样例,删和铺的处理顺序没有处理好。

于是尝试着改变标记的含义,vi表示对于这一块之前的它要删去多少,pi表示这一块删除操作结束以后它铺了多少。和官方题解的tag含义好像不一样又好像一样我没搞清楚…反正很好操作。合并的时候如果右儿子没有删除,就把左儿子的vi累计过来,左右儿子的pi都加上。如果左边的pi不够右边的vi删掉,就累计右边的pi,加上左右儿子的vi并减去左边的pi。比较麻烦的是左边的pi大于右边vi的时候,这个地方没法直接合并因为没法直接知道要删哪些而统计哪些的贡献,所以sum类加上右儿子的sum,再写个work函数处理左儿子,把右边的vi传进去作为v。vi统计为左儿子的vi,pi统计为左儿子的pi减去右儿子的vi再加上右儿子的pi。

在work函数里,如果当前节点【刚传进去的时候是update里处理到的左儿子】的右儿子的pi正好等于v,那么return当前节点的sum减去右儿子的sum。因为当前节点的sum已经是被左右儿子的pivi处理过的,左儿子的sum可能有被削去,但右儿子的sum一定是直接累加进来的,减一下就是剩余的贡献。如果右儿子的pi小于v,那么不够减,再递归处理当前节点的左儿子。这个时候v要减去右儿子的pi,但还要加上右儿子的vi,因为相当于重新处理左儿子。而右儿子pi大于v的时候,要递归处理右儿子,并且已经可以直接累加右儿子以外的贡献了。所以仿照刚刚的情况,用当前的sum减去右边的sum,再加上work右边,传进work的v不变。这一块v的处理很像平衡树根据排名找权值,sum要考虑当前节点是已经被处理过的并且左儿子的贡献不能直接算进来。

然后瞎搞一通交上去,居然是OK的,还抢了第一个AC的绿色方块:D但是后面的时间没剩下多少了,导致T3爆炸……

#include<iostream>
#include<cstdio>
using namespace std;
int m,q,kk,vv;
struct node{
    int k,v;
}a[200010];
struct tree{
    int l,r,sum,pi,vi;
}b[800010];
int work(int p,int v){
    if(b[p*2+1].pi<v){
        return work(p*2,v-b[p*2+1].pi+b[p*2+1].vi);
    }
    else if(b[p*2+1].pi==v){
        return b[p].sum-b[p*2+1].sum;
    }
    else{
        return b[p].sum-b[p*2+1].sum+work(p*2+1,v);
    }
}
void update(int p){
    if(!b[p*2+1].vi){
        b[p].sum=b[p*2].sum+b[p*2+1].sum;
        b[p].vi=b[p*2+1].vi+b[p*2].vi;
        b[p].pi=b[p*2].pi+b[p*2+1].pi;
    }
    else if(b[p*2+1].vi>=b[p*2].pi){
        b[p].sum=b[p*2+1].sum;
        b[p].pi=b[p*2+1].pi;
        b[p].vi=b[p*2+1].vi-b[p*2].pi+b[p*2].vi;
    }
    else{
        b[p].sum=b[p*2+1].sum+work(p*2,b[p*2+1].vi);
        b[p].pi=b[p*2+1].pi+b[p*2].pi-b[p*2+1].vi;
        b[p].vi=b[p*2].vi;
    }
}
void build(int p,int l,int r){
    b[p].l=l,b[p].r=r;
    if(l==r){
        if(!a[l].k){
            b[p].sum=a[l].v;
            b[p].pi=1,b[p].vi=0;
        }
        else{
            b[p].sum=0;
            b[p].pi=0;
            b[p].vi=a[l].v;
        }
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    update(p);
}
void change(int p,int l,int r){
    if(l<=b[p].l&&b[p].r<=r){
        if(!kk){
            b[p].sum=vv;
            b[p].pi=1,b[p].vi=0;
        }
        else{
            b[p].sum=0;
            b[p].pi=0;
            b[p].vi=vv;
        }
        return;
    }
    int mid=(b[p].l+b[p].r)/2;
    if(l<=mid)change(p*2,l,r);
    if(r>mid)change(p*2+1,l,r);
    update(p);
}
int main()
{
//    freopen("3.in","r",stdin);
//    freopen("3.out","w",stdout);
    scanf("%d%d",&m,&q);
    for(int i=1,k,v;i<=m;i++){
        scanf("%d%d",&a[i].k,&a[i].v);
    }
    build(1,1,m);
    while(q--){
        int c;
        scanf("%d%d%d",&c,&kk,&vv);
        change(1,c,c);
        printf("%d
",b[1].sum);
    }
    return 0;
}
T2

T3Drink:

第一遍看题的时候对题意的理解还是对的,搞完前两题再来看居然写着写着就偏了,打了个错误的模拟爆零滚蛋。

题面:整个正方形顺时针转…

我:一个正方形的边顺时针转…

两次考试看错两道题OTZ我去看看眼睛再回来学习

然而因为马上就要走了,先颓博客,上一篇题解的T3还没写,所以咕咕咕:P

总的来说比我想得要好,这次发挥大约还算可以,大约是上帝在我想放弃的时候一脚把我踹了回来。

大概和oi以及一起学习的oier们有某种奇特的缘分吧,说不定呢。

原文地址:https://www.cnblogs.com/chloris/p/11330881.html