20231103 比赛总结

比赛经过

日常 20 m i n 20min 20min 后开考
稍微想了一下 T 1 T1 T1,手玩了几个数据 + 样例的树形结构发现和 l o w b i t lowbit lowbit 关系较大,遂往这个方向考虑,大概回了,写了很久,中间有些细节不确定调了很久,大概 1 h 1h 1h 过了
T 2 T2 T2 一眼感觉第二种法术在最下面的根号左右层才有用,算了一下发现是 6 n \sqrt {6n} 6n ,有 800 800 800 左右, n f l s nfls nfls 机子很慢,在乘上一些常数感觉有点过不去
去和其他人讨论了一下发现没有更好的解法, 遂冲了一发,很快过了大样例,测了一波极限数据 0.3 s 0.3s 0.3s,怎么跑这么快!!!这时大概是 2 h 2h 2h
T 3 T3 T3 题目很长,花了一会才懂,是个缝合怪题,第二部分很快会了 q l o g n qlogn qlogn 做法,但第一个问题 n*m的网格中有k个矩形,对每一行求出有多少个矩形被覆盖了<lim次(lim<=10) 一直只有 O ( n n ) O(n\sqrt n) O(nn ) 的想法,差不多 2 h 50 m i n 2h50min 2h50min 左右,还没想出来,于是开始写,到 3 h 20 m i n 3h20min 3h20min 左右调完,过了大样例 1 − 5 1-5 15,大概能拿 65 p t s 65pts 65pts
想摆,看了一眼部分分, l i m = 1 lim=1 lim=1,那不直接线段树维护最小值吗,诶,我好像会了,直接线段树维护前 l i m lim lim 个最小值及出现次数不就可以了吗,还有 50 m i n 50min 50min,于是开始冲,在还剩 20 m i n 20min 20min 时过了所有样例,但最大样例有点慢,卡了卡常,放在 n f l s nfls nfls 上测了一下,很快,遂摆
预估分数: 100 + 100 + 100 + 5 = 305 100+100+100+5=305 100+100+100+5=305
实际分数: 100 + 100 + 100 + 5 = 305 100+100+100+5=305 100+100+100+5=305

反思

T 2 T2 T2 想到根号做法却因为常数问题浪费了 30 m i n 30min 30min 没冲是一个错误
感觉自己一些普通问题上的思考也不够,感觉线段树维护 l i m lim lim 小值很板

题解

A

没什么好说的,直接组合数算一下

#include <bits/stdc++.h>
using namespace std;
const int N=10000100,K=1000100,P=998244353;
int n,q,fac[N],inv[N],v[K];
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
int binom(int a,int b){
    if(a<0||b<0||a<b) return 0;
    return 1ll*fac[a]*inv[b]%P*inv[a-b]%P;
}
int qmi(int a,int b){
    int res=1;
    for(;b;b>>=1){
        if(b&1) res=1ll*res*a%P;
        a=1ll*a*a%P;
    }
    return res;
}
inline void inc(int &x,int y){ x+=y;if(x>=P) x-=P;}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    fac[0]=1;
    for(int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%P;
    inv[N-1]=qmi(fac[N-1],P-2);
    for(int i=N-2;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%P;
    n=read(),q=read();
    while(q--){
        int k=read();
        for(int i=1;i<=k;i++) v[i]=read();
        v[++k]=n;
        int d=read(),ans=0;
        for(int i=1;i<=k;i++){
            int curd=d-(i-1);
            if(i>1) inc(ans,binom(v[i],curd)),inc(ans,P-binom(v[i-1],curd-1));
            else inc(ans,binom(v[i],curd));
        }
        printf("%d\n",ans);
    }
    fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    return 0;
}

T2

观察到第二种法术只有在后 6 n \sqrt{6n} 6n 层才会施展,于是直接暴力 d p dp dp 即可,状态是 f i , j f_{i,j} fi,j 表示到第 i i i 列,施展第二种法术的位置的 m a x x + y = j max{x+y}=j maxx+y=j 的最小代价

#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
using namespace std;
const int N=100100,B=800,inf=1e9;
typedef pair<int,int> pii;
int n,k,sum[B+100],ans[B+100];
pii p[N];
vector<int> vec[N];
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
int main(){
    freopen("tower.in","r",stdin);
    freopen("tower.out","w",stdout);
    n=read(),k=read();
    for(int i=1;i<=k;i++) p[i].x=n-read()+1,p[i].y=read();
    sort(p+1,p+k+1);
    int tot=0;
    for(int i=1;i<=k;i++){
        if(p[i].x>B) tot+=3;
        else vec[p[i].y].pb(p[i].x);
    }
    memset(ans,0x3f,sizeof(ans));ans[0]=0;
    for(int i=1;i<=n;i++){
        int y=n-i+1,lim=min(B,y);
        for(int x:vec[i]) sum[x]++;
        sum[lim+1]=0;
        for(int j=lim;j;j--) sum[j]+=sum[j+1];
        int mn=inf;
        for(int j=0;j<=lim;j++){
            int t=ans[j];
            ans[j]=min(ans[j],j*(j+1)/2+2+mn)+3*sum[j+1];
            mn=min(mn,t);
        }
        int t=ans[0];
        for(int j=0;j<=lim;j++) ans[j]=ans[j+1];
        ans[0]=min(ans[0],t),ans[lim]=inf;
        for(int j=0;j<=lim;j++) sum[j]=0;
    }
    printf("%d\n",ans[0]+tot);
    fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    return 0;
}

C

首先第二问是好处理的,直接按照 m i n min min 男女的数量排序,处理一下前缀和即可
考虑第一问也就扫描线,然后用线段树维护前 l i m lim lim 小值和其出现次数即可
时间复杂度 O ( n k l o g n + q l o g n ) O(nklogn+qlogn) O(nklogn+qlogn)

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long LL;
const int N=300100;
struct RANGE{ int l,r,neg;};
int n,m,c,lim,q,v[N];
LL sum1[N],sum2[N];
vector<RANGE> range[N];
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
struct Node{ int mn[10],times[10];}seg[N<<2];
int tag[N<<2];
void down(int x,int tg){ for(int i=0;i<lim;i++) seg[x].mn[i]+=tg;tag[x]+=tg;}
void build(int l,int r,int x){
    seg[x].mn[0]=0,seg[x].times[0]=r-l+1;
    for(int i=1;i<lim;i++) seg[x].mn[i]=1e9,seg[x].times[i]=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,x<<1),build(mid+1,r,x<<1^1);
}
void pushdown(int x){
    if(tag[x]) down(x<<1,tag[x]),down(x<<1^1,tag[x]),tag[x]=0;
}
void pushup(Node &ret,Node lc,Node rc){
    for(int i=0,j=0,k=0;i<lim;i++){
        if(lc.mn[j]==rc.mn[k]) ret.mn[i]=lc.mn[j],ret.times[i]=lc.times[j]+rc.times[k],j++,k++;
        else if(lc.mn[j]<rc.mn[k]) ret.mn[i]=lc.mn[j],ret.times[i]=lc.times[j],j++;
        else ret.mn[i]=rc.mn[k],ret.times[i]=rc.times[k],k++;
    }
}
void modify(int l,int r,int x,int L,int R,int v){
    if(L<=l&&r<=R){ down(x,v);return;}
    pushdown(x);
    int mid=(l+r)>>1;
    if(mid>=L) modify(l,mid,x<<1,L,R,v);
    if(mid<R) modify(mid+1,r,x<<1^1,L,R,v);
    pushup(seg[x],seg[x<<1],seg[x<<1^1]);
}
int main(){
    freopen("army.in","r",stdin);
    freopen("army.out","w",stdout);
    n=read(),m=read(),c=read(),lim=read(),q=read();
    for(int i=1;i<=c;i++){
        int x1=read(),y1=read(),x2=read(),y2=read();
        range[x1].pb({y1,y2,1}),range[x2+1].pb({y1,y2,-1});
    }
    build(1,m,1);
    for(int i=1;i<=n;i++){
        for(auto ran:range[i]) modify(1,m,1,ran.l,ran.r,ran.neg);
        auto fin=seg[1];
        int res=0;
        for(int j=0;j<lim&&fin.mn[j]<lim;j++) res+=fin.times[j];
        v[i]=min(res,m-res);
    }
    sort(v+1,v+n+1);
    for(int i=1;i<=n;i++) sum1[i]=sum1[i-1]+v[i],sum2[i]=sum2[i-1]+1ll*v[i]*v[i];
    while(q--){
        int x=read(),y=read();
        LL ans=0;
        int pos=lower_bound(v+1,v+n+1,y/2)-v;
        if(n-pos+1>=x) ans=1ll*x*(y/2)*(y-y/2);
        else ans=1ll*(n-pos+1)*(y/2)*(y-y/2)+1ll*y*(sum1[pos-1]-sum1[n-x])-(sum2[pos-1]-sum2[n-x]);
        printf("%lld\n",ans);
    }
    fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    return 0;
}

D

没补,不会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值