Codeforces Round 573 (Div.1) 题解

这场怎么说呢……有喜有悲吧。

开场先秒了 A。看到 B,感觉有点意思,WA 了 2 发后也过了。

此时还在 rk 前 200。

开 C,一看就不可做。跟榜,切 D 人数是 C 的两倍。

开 D。一眼感觉很 SB,然后就想了个假做法,WA 了 3 发。

1:10 时开始重构。再 WA1 发。结果 WA 了 4 发,才过掉。

怎么全世界的 D 都比我高分……

system test 前 predictor 说我的 rating 变化是……0。顿时很慌。

幸好 B 题 FST 了一片(DQ 和 deco 也 FST 了),rk 从 350+ 翻到了 320+。

最后 rating+=8。我还是太菜了……


A

直接模拟即可。每次找到最小的还没被删的东西,看看能删到哪里。可以 $O(m)$。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
    char ch=getchar();ll x=0,f=0;
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int m,ans;
ll n,k,p[maxn];
int main(){
    n=read();m=read();k=read();
    FOR(i,1,m) p[i]=read();
    FOR(i,1,m){
        ll at=(p[i]-i+k)/k;
        int j=i;
        while(j<=m && p[j]-i+1<=at*k) j++;
        j--;
        i=j;ans++;
    }
    printf("%d\n",ans);
}
View Code

B

先判先手能不能走第一步:

  • $0$ 出现了至少两次,不行。
  • 有数出现了至少三次,不行。
  • 有数($x$)出现了至少两次,且满足条件的 $x$ 不止一个,不行。
  • 有数($x$)出现了至少两次,且 $x-1$ 也出现了,不行。(这个情况 pretest 没有,所以 FST 了一片)、
  • 否则就可以。

如果先手能走第一步,那么可以证明最后状态肯定是 $0$ 到 $n-1$ 的一个排列。判下奇偶性就可以了。

时间复杂度 $O(n\log n)$,因为要排序/map。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
    char ch=getchar();ll x=0,f=0;
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int n,a[maxn];
bool hhh;
ll s;
int main(){
    n=read();
    FOR(i,1,n) s+=a[i]=read();
    sort(a+1,a+n+1);
    if(!a[1] && a[1]==a[2]) return puts("cslnb"),0;
    FOR(i,2,n-1) if(a[i]==a[i-1] && a[i]==a[i+1]) return puts("cslnb"),0;
    FOR(i,2,n-1) if(a[i]==a[i+1] && a[i]==a[i-1]+1) return puts("cslnb"),0;
    FOR(i,1,n-1) if(a[i]==a[i+1]){
        if(hhh) return puts("cslnb"),0;
        hhh=true;
    }
    puts((s-1ll*n*(n-1)/2)&1?"sjfnb":"cslnb");
}
View Code

C

太神仙了吧……先咕着。


D

全部离散化。

枚举最小的 $y=y'$。那么只用考虑所有在 $y=y'$ 上及其上方的点。

假设 $y=y'$ 的点有 $(x_1,y'),(x_2,y')\dots(x_k,y')$,那么这些答案的和就是 $f(cnt(1,szx,y'))-f(cnt(1,x_1-1,y'))-f(cnt(x_k+1,szx,y'))-\sum\limits_{i=1}^{k-1}f(cnt(x_i+1,x_{i+1}-1,y'))$。

注意,离散化过了,离散化后所有点的最大 $x$ 坐标是 $szx$。$f(x)=\frac{x(x+1)}{2}$,也就是长度为 $x$ 的区间有多少个子区间。$cnt(l,r,a)$ 表示 $x$ 坐标在 $[l,r]$ 的点中有几个的 $y$ 坐标 $\ge a$。

(建议画图理解)

可以上扫描线+树状数组。从上往下扫。树状数组维护哪些列上有点。$cnt(l,r,y')$ 就是个区间求和。每次对于每个点,如果它所在的列还没被加入树状数组中,加入。

时间复杂度 $O(n\log n)$。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=200020;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
    char ch=getchar();ll x=0,f=0;
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int n,x[maxn],y[maxn],tmpx[maxn],tmpy[maxn],szx,szy,b[maxn],cnt[maxn];
vector<int> xs[maxn];
ll ans;
void update(int p,int v){
    for(int i=p;i<=szx;i+=i&-i) b[i]+=v;
}
int query(int p){
    int s=0;
    for(int i=p;i;i-=i&-i) s+=b[i];
    return s;
}
int query(int l,int r){
    if(l>r) return 0;
    return query(r)-query(l-1);
}
int main(){
    n=read();
    FOR(i,1,n) tmpx[i]=x[i]=read(),tmpy[i]=y[i]=read();
    sort(tmpx+1,tmpx+n+1);szx=unique(tmpx+1,tmpx+n+1)-tmpx-1;
    sort(tmpy+1,tmpy+n+1);szy=unique(tmpy+1,tmpy+n+1)-tmpy-1;
    FOR(i,1,n) x[i]=lower_bound(tmpx+1,tmpx+szx+1,x[i])-tmpx,y[i]=lower_bound(tmpy+1,tmpy+szy+1,y[i])-tmpy;
    FOR(i,1,n) xs[y[i]].push_back(x[i]);
    FOR(i,1,szy) sort(xs[i].begin(),xs[i].end());
    ROF(i,szy,1){
        FOR(j,0,(int)xs[i].size()-1) if(++cnt[xs[i][j]]==1) update(xs[i][j],1);
        int len=query(1,szx);
        ans+=1ll*len*(len+1)/2;
        len=query(1,xs[i][0]-1);
        ans-=1ll*len*(len+1)/2;
        len=query(xs[i][(int)xs[i].size()-1]+1,szx);
        ans-=1ll*len*(len+1)/2;
        FOR(j,0,(int)xs[i].size()-2){
            len=query(xs[i][j]+1,xs[i][j+1]-1);
            ans-=1ll*len*(len+1)/2;
        }
    }
    cout<<ans<<endl;
}
View Code

E

PB 说这是个 SB 题,没有思维难度,比 C 还简单……我说我不会还被喷了 QwQ

回来再说吧。


F

其实说句实话,这辈子都没想过改出 Div.1 的 F。不管了。

 

转载于:https://www.cnblogs.com/1000Suns/p/11179833.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值