比赛经过
日常
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
没补,不会