2021/9/19 icpc网络赛A,D题解+赛时记录

本篇前半部分是(赛时)我写的题(有三道,但是有道签到就不放出来了)
后半部分是个人的五个小时记录吧

A .A Busiest Computing Nodes

题目大意:给定k个电脑和n组文件,
电脑的编号是0 ~ k-1,文件的编号是0 ~ n-1
每个文件开始时间为 at,需要运行时间为pt,一台电脑同时只能处理一个文件。
对第i个文件,将从( i % k )台电脑开始,(向后环状)找到第一台未运行的电脑,将文件放入电脑处理,若无可放入电脑则将文件丢弃,从小到大输出处理最多文件的电脑编号。
1 ≤ k , n ≤ 1e5
1 ≤ at , pt ≤1e9
数据保证所有文件的开始时间非降序

题解:题目的核心目标是找到每个电脑的插入位置,找到插入位置前要处理所有电脑的工作状态,显然最暴力的做法下,即为对于每个读取的文件,先遍历所有电脑,判断该电脑此时状态(对于工作中的电脑,记录一个结束时间,如果结束时间早于开始时间就停止工作),然后再从( i % k )开始循环遍历一遍电脑进行查找插入位置。对于这样的暴力操作,我们容易算的复杂度为O(nk),显然是不行的,于是我们便思考对这种暴力的优化。

首先有一个常用的技巧,一个统计工作状态的01数组(工作为1,非工作为0),如果对于该数组的一个区间和等于区间长度,容易知道这个区间所有电脑都在工作中。在此基础上我们便可以通过二分找到第一个非工作的电脑。

回到这题,我们容易发现可以通过这种方式找到位置,此时,我们需要满足单点修改和区间求和两种操作,所以树状数组是我们的首选(树状数组+二分复杂度是lognlogn,这里如果使用线段树的话,它本身自带二分结构,可以少掉二分的log,但是树状数组本身常数较小,而且也好写,在不卡常的时候还是很好用的)。现在,第一个任务目标完成了。

接着是处理各电脑的工作状态,实现的方式有很多,我们队的想法是,用维护一个按结束时间排序的小根堆,然后每次获得文件时,把结束时间早于当前文件开始时间的电脑置为0,堆的操作复杂的时log,树状数组更新也是log,合起来就是log log。

综上所述,此题就只需要将上述过程拼接即可,复杂度是( n * logk *logk)
(代码由于经过多人修改所以码风变化很大)
(而且此题卡行末空格极其傻逼)

#include <bits/stdc++.h>
#define lowbit(x) (x)&(-x)
using namespace std;
const int MAXN = 2e5 + 10;
int a[MAXN], len;
int tree[MAXN], sum[MAXN];
int ans,tot=0;
inline void init(){
    memset(tree, 0, sizeof(tree));
}
inline void update(int i, int x){
    for(int pos = i; pos <= len; pos += lowbit(pos)) tree[pos] += x;
}
inline int getsum(int i){
    int ans = 0;
    for(int pos = i; pos; pos -= lowbit(pos)) ans += tree[pos];
    return ans;
}
inline int query(int l, int r){
    return getsum(r) - getsum(l - 1);
}
struct node{
    int ser, aend;
    bool operator < (const node &x) const{
        return aend >x.aend;
    }
};
priority_queue<node> q;
signed main(){
    int k, n; cin >> k >> n;
    init();
    len=2*k;
    for(int i = 0; i < n; i++){
        int at, pt; cin >> at >> pt;
        while(!q.empty()){
            if(q.top().aend<=at){
                update((q.top().ser+k-1)%(2*k)+1,-1);
                update(q.top().ser,-1);
                q.pop();
            }
            else{
                break;
            }
        }
        //找到插入位置id
        int l = i % k + 1, r = 2 * k;
        while(l <= r){
            int mid = (l + r) >> 1;
            int res = query(l, mid);
            if(res == mid - l + 1) l = mid + 1;
            else r = mid - 1;
        }
        if(l-(i%k+1)<=k){
            q.push(node{l, at + pt});
            update(l, 1);
            update((l+k-1)%(2*k)+1,1);
            sum[l]++;
            sum[(l+k-1)%(2*k)+1]++; 
        }
    }
    for(int i=1;i<=k;i++){
        if(sum[ans]<sum[i]){
            ans=i;
        }
    }
    for(int i=1;i<=k;i++){
    	if(sum[ans]==sum[i]){
    		if(tot++){
	            cout<<" ";
	        }
    		cout<<i-1;
		}
	}
    cout<<endl;
    return 0;
}

D.Edge of Taixuan

题目大意: t组输入,给定n个点和m组操作,每次操作给一个l,r,w,为在【l,r】中建一个完全图,边权为w,求建完所有边后其所有的边与其最小生成树的差。

题解 :分块暴力跑,就结果来看还挺快,具体想法后面有人想看再补

#include <bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m,d,ans,tot,cnt,sum,op;
int blocks[100005],vis[100005],blockvis[1005],a[100005],blocksum[1005],blocksize[1005];
struct node{
    int l,r,w;
    bool operator < (const node a)const{
        return w<a.w;
    }
}p[100005];
signed main(){
    cin>>t;
    while(t--){
        cin>>n>>m;
        n--;
        tot=cnt=1;
        sum=ans=0;
        d=sqrt(n);

        for(int i=1;i<=n;i++){
            vis[i]=0;
        }
        for(int i=1;i<=n;i++){
            blocks[i]=cnt;

            if(tot==d){
                tot=1;
                cnt++;
            }
            else{
                tot++;
            }
        }

        for(int i=1;i<=cnt;i++){
            blockvis[i]=blocksum[i]=blocksize[i]=0;
        }
        for(int i=1;i<=n;i++){
            blocksize[blocks[i]]++;
        }
        for(int i=1;i<=m;i++){
            cin>>p[i].l>>p[i].r>>p[i].w;
            ans+=(p[i].r-p[i].l)*(p[i].r-p[i].l+1)/2*p[i].w;
            p[i].r--;
        }
        sort(p+1,p+1+m);

        for(int i=1;i<=m;i++){
            if(blocks[p[i].l]==blocks[p[i].r]){
                if(blockvis[blocks[p[i].l]]!=blocksize[blocks[p[i].l]]){
                    for(int j=p[i].l;j<=p[i].r;j++){
                        if(!vis[j]){
                            sum+=p[i].w;
                            vis[j]=1;
                            blockvis[blocks[j]]+=1;
                        }
                    }
                }
            }
            else{
                if(blockvis[blocks[p[i].l]]!=blocksize[blocks[p[i].l]]){
                    for(int j=p[i].l;blocks[j]==blocks[p[i].l];j++){//前区间修改
                        if(!vis[j]){
                            sum+=p[i].w;
                            vis[j]=1;
                            blockvis[blocks[j]]+=1;
                        }
                    }
                }
                if(blockvis[blocks[p[i].r]]!=blocksize[blocks[p[i].r]]){
                    for(int j=p[i].r;blocks[j]==blocks[p[i].r];j--){//后区间修改
                        if(!vis[j]){
                            sum+=p[i].w;
                            vis[j]=1;
                            blockvis[blocks[j]]+=1;
                        }
                    }
                }

                for(int j=blocks[p[i].l]+1;j<blocks[p[i].r];j++){//完整区间修改
                    if(blockvis[j]!=blocksize[j]){
                        sum+=(blocksize[j]-blockvis[j])*p[i].w;
                        blockvis[j]=blocksize[j];
                    }
                }
            }
        }
        cout<<"Case #"<<++op<<": ";
        int flag=0;
        for(int i=1;i<=cnt;i++){
            if(blockvis[i]!=blocksize[i]){
                cout<<"Gotta prepare a lesson"<<endl;
                flag=1;
                break;
            }
        }
        if(!flag){
            cout<<ans-sum<<endl;
        }
    }
    return 0;
}

别的题题解队友写了后面会放链接在这(过着过段时间我自己来补)

小作文阶段:(隔了好几天具体细节可能有所疏漏)
现在是2021年9月22日两点十分,据icpc第一场网络赛打完已经两天多了,为什么没有在结束的第一天写这些东西呢,主要还是这几日的假期属实不想学习,而且这场网络赛给人的体验过于糟糕,没有写东西的心情。请添加图片描述
我对这场比赛的评价是:这他妈是个啥啊。(卡输出,卡输入,还有阅读理解题面)
至于我们队的发挥呢,队伍比赛的结果还算中规中矩,写了六道题,而且在结束的时候还压了已经出算法的一题没写=时间写,只能说挺满意,但是还有很大的进步空间。

以下主角为队友Z 和队友Y 以及我自己

我主要负责签到所以我一开始就坐到了键盘前。

开场前10min,得知纸质版题面只能加急打印运送,开始时只能读网站上的题面,我们队内讨论了一下开局该干啥,最终我们决定先过一遍全部的题面,不开题,然后跟榜写题。

比赛开始,点开A,30s过后队友Y(我队读题核心)说大概知道了,让我们看B,一起看了几秒钟B发现凸包两个字,打了个计算几何的标签就接着看后面的题了。

按照中长题面和傻逼输入输出先不看的准则,跳过了CDE连续三题,看到了F,一眼看到点坐标觉得又是一道计算几何,打上标记。继续疯狂找短题面发现后面只有I题能先读。看时间发现已经7min了于是想看榜去选择跟榜,但是一下子榜没加载出来,我们三人以为是还没人写出签到题,此时非常慌张,我队三人上赛季末刚在沈阳因为阅读理解能力极差打铁,看完题目深感不妙,吐槽了一下不会又是阅读理解场吧遂读I。读完发现了I的签到性质,此时已经是10min+,再去看榜,发现榜上还没有人(其实是还没加载出来)此时纸质题面还未送到,两个队友只好看着我写I,(写道一半送到了)光速写完代码提交一发过,开了个好头。

队友Z在看F,队友Y在看A,Z拉着我去讨论F题意,此时并没有发现数据给的特殊点位和数据范围,全按照一般的办法想着A圆上哪个点到圆点和b点最短,看到答案输出两位小数,队友Z说好像是个if判断但是不好证明,我直接口胡了一个用枚举圆上1w个点取最小的算法还以这枚举的点一定包括你特判的点,队友觉得没问题就让我写了一发,毫无疑问的wa了(赛后讨论觉得是爆ll了导致取min时炸了),喜提罚时*1。扛着队友杀人的眼神,删了之前的代码,抱着怀疑写了发简单的判断a了,演队友次数++。

看榜发现此时榜上都在写A题,此时Y说A题意出来了但是没思路,他把题意喂给我们后我排除万难,不断优化花了好久才想到了上述题解思路,兴奋的和队友讲,队友Y觉得没问题决定按照我的思路写,解放队友Z和我去开题。接下来就是无尽的折磨,队友Z和我的英语水平差的离谱,(根本没有英语阅读能力.jpg)我开了B,队友开了H。读完B后发觉队内三人都不会凸包就直接放弃,看到Y仍然在写A就看着他写顺便随时答疑()。但是队友Y在写树状数组更新的时候好像还是不能明白怎么实现的,只能重新上机去完成A题代码,此时Y前去看K。

此时进入我的绝望1h,中途遇到的问题包括但不限于

交第一次前
①发现只能过样例,别的啥都过不去(主要原因是所有测试数据都输出1) 解决方法是树状数组队友用的板子,里面有个长度是len,但是队友没有给len赋值
②树状数组查询时r-(l-1),所以l=0的时候会爆错,直接导致所有下标要+1,大改程序
③堆建反了(结构体小于号重载反了)
提交答案
①提交后发现答案错误(询问队友发现输出可能有多个,而且在前一次问队友多个最大是否输出最大,队友嗯了一下)
②更改输出后提交发现格式错误
③心态爆炸删除最后一个空格发现答案错误
④因为调试要把sum开小不然显示不了但交答案的时候忘改回去交上去段错误
这道题是我本场演队友最严重的题,直接喜提四发罚时,a的时候直接跑到3题尾去了。

赛程过半,队友在我调a期间开分屏写了不知道哪题的代码,写完后队友Z让我去看H,结合队友给的题意,读完后深感自己读错题,跟队友吐槽不会真有rz出题人整无用数据吧,抱着怀疑的态度,队友写了一发,我亲眼看着队友Z从开始的光速打代码转到龟速调输出,顺带疯狂头顶问号询问出题人到底要我们干啥。交了一遍后发现wa了,沉下心来重新读题和代码,结果发现是输出中有个傻逼询问的编号,加上之后直接a了,全队感觉这题莫名其妙。

此时再看榜发现剩下几题过题人数差不多便三人开BDK,我读B的时候细想了一下发现如果我会凸包也不知道怎么整这个跟谜语一样的输入和非法输入,于是就和Z去讨论D题了,中途Z被Y拉去讨论K题做法。他们讲完之后Y上机光速码题,我和Z开始对D的算法进行了争吵,我个人倾向线段树维护一个区间状态确认区间的值是否完全赋值,队友则是一个乱七八糟的最小生成树的写法(可能他听我的也是乱七八糟的),我们想的算法都是半成品但是现在也不知道如何优化了。队友Y此时光速码完K询问我们是否交一遍,想着直接冲的想法交了一发结果过了感觉还行。

此时问队友是否还有别的题能写队友说暂时没有,算了下数据感觉分块写D好像还行于是我便直接上机写分块,队友就看榜看别的题。分块途中状况挺多,比如发现自身算法不成熟要加很多数组来维护,但是还是磕磕绊绊的写完了。而且在还剩40min的时候,两个队友似乎已经讨论出了一道字典树板子题,想等我把分块写完调完就写。龟速写完发现提交发现wa,摸了几个数据发现只能过样例,别的好多都过不去,查了许多时间结果发现是算初始值的时候式子写错了,便改掉,此时发现只剩10min不到了,队友最后的题也写不了了。

剩下时间没在写题,只好看榜,找了下熟人的队伍发现基本都是7-8道,a了6题的快乐突然无了QAQ。

只能说各种阅读理解和卡输出格式太让人难以接受了,剩下的就不对比赛做过多评价了= =。

以下是提交记录
请添加图片描述

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值