比赛经过
日常 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 1−5,大概能拿 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
没补,不会