2021.2.27模拟赛总结

开场看A,想了一会发现是个简单的数据结构题。
但是要写个分块+半平面交。
由于以前模拟赛场上写分块+半平面交写整场获得0分的好成绩的教训,所以想了一会怎么转成凸包。
看B后一会想出了80分做法。
然后想了一会100分,不太会
然后回头写A。
A的分块+凸包写的很不自信,写完后一直过不了样例。
想了一会C,发现也不太会。
然后写了个裸暴力打算进行对拍,发现裸暴力也错了。
搞了好久才发现样例错了,浪费了好多时间。
感觉时间不多了,于是写了个B的20分和C的10分。
准备写B的80分,但是发现细节有点多。
感觉离调出A不久了。
然后就一直调到比赛结束。
最后只有30分。。。。。
总结:由于A的样例出错浪费了大量时间。
而且由于分块好久没写了,导致A调很久。
这说明以后要多写分块,减少调试时间。
而且,对计算几何(凸包)不熟悉
题解:
A:从小到大枚举(c)
问题转化成:维护一个数据结构,支持插入一个值,询问(p*a_i),其中(p)(geq i)的有值位置数。
考虑维护一数组(b),如果插入一个位置(v),把(b_{1...v}++)
我们需要查询(max(b_i*a_i))
考虑凸包。我们要支持区间加法。
对于整块的加法可以预处理出每块的半平面交然后移动指针。
对于散块的加法考虑重构,我们要把直线(a_ix+y)变成(a_ix+y+v)
发现斜率是不会变的,所以建立凸包不用排序
时间复杂度(O(nsqrt{n}))

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 100010
int n,w,mx,ans[N],vi[N],l[N],r[N],ss,bb[N],id[N],tg[N],tt[N];
struct no{
	int x,y,id;
}a[N],b[N];
int va(no x,int y){
	return x.x*y+x.y;
}
int operator <(no x,no y){
	return x.y<y.y;
}
int cc(no x,no y){
	return x.x<y.x;
}
int cp(no j,no k,no i){
	return (k.y-j.y)*(i.x-k.x)>=(k.x-j.x)*(i.y-k.y);
}
struct hl{
	no s[500];
	int tp,pt,l,r,id;
	void ps(no x){
		if(tp<2){
			s[++tp]=x;
			return;
		}
		while(tp>1&&cp(s[tp-1],s[tp],x))
			tp--;
		s[++tp]=x;
	}
	void rb(){
		tp=0;
		for(int i=l;i<=r;i++)
			if(vi[i]){
				no va;
				va.x=b[i].x;
				va.y=(-tg[id]-tt[i])*b[i].x;
				ps(va);
			}
		pt=1;
	}
	int qu(int x){
		if(!tp)
			return 0;
		while(pt<tp&&va(s[pt],x)>=va(s[pt+1],x))
			pt++;
		return -va(s[pt],x);
	}
}bl[500];
int qu(){
	int ans=0;
	for(int i=1;i<=ss;i++)
		ans=max(ans,bl[i].qu(-tg[i]));
	return ans;
}
void ad(int c,int d){
	for(int i=1;i<=ss;i++){
		if(c<=l[i]&&r[i]<=d)
			tg[i]++;
		else if(d>=l[i]){
			for(int j=max(c,l[i]);j<=min(d,r[i]);j++)
				tt[j]++;
			for(int j=l[i];j<=r[i];j++)
				tt[j]+=tg[i];
			tg[i]=0;
			bl[i].rb();
		}
	}
}
void ins(int x,no y){
	vi[x]=1;
	b[x]=y;
	for(int i=l[bb[x]];i<=r[bb[x]];i++)
		tt[i]+=tg[bb[x]];
	tg[bb[x]]=0;
	bl[bb[x]].rb();
}
signed main(){
	freopen("laofu.in","r",stdin);
	freopen("laofu.out","w",stdout);
	scanf("%lld%lld",&n,&w);
	int bs=sqrt(n),po=1,i=1;
	while(po<=n){
		l[i]=po;
		r[i]=min(po+bs-1,n);
		po+=bs;
		i++;
	}
	ss=i-1;
	for(int i=1;i<=ss;i++){
		bl[i].l=l[i];
		bl[i].r=r[i];
		bl[i].id=i;
	}
	for(int i=1;i<=ss;i++)
		for(int j=l[i];j<=r[i];j++)
			bb[j]=i;
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&a[i].x,&a[i].y);
		b[i]=a[i];
		mx=max(mx,a[i].y);
	}
	int pt=1;
	sort(b+1,b+n+1,cc);
	for(int i=1;i<=n;i++)
		id[b[i].x]=max(id[b[i].x],i);
	for(int i=1;i<=n;i++){
		a[i]=b[i];
		a[i].id=i;
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
		b[i].y=b[i].x=0;
	for(int c=1;c<=mx+1;c++){
		while(a[pt].y<c&&pt<=n){
			ins(a[pt].id,a[pt]);
			ad(1,id[a[pt].x]);
			pt++;
		}
		printf("%lld ",qu()+(n-pt+1)*w*c);
	}
}

C:
看了题解感觉不是很难,但是不知道为什么不会做。
想到了字符数量取差,想到前缀和就是想不到正解。
先考虑字符集为(2)的情况,令(f_i)表示(s_{1...i})(a)个数和(b)个数的差。
一个字符串(s_{j...i})的差是(f_i-f_{j-1}),要求(-k leq f_i-f_{j-1}leq k)
发现(i,j)调换不影响答案。
所以合法条件是:(max(f_i)-min(f_i) leq k)
考虑枚举(min(f_i)),就能知道(max(f_i))的取值范围((min(f_i)+k)),可以用(k)次dp计算。
(f_{i,j})表示填了(i)个字符,最大值为(j)的答案,可以用矩阵乘法计算。
发现这样子会算重,(max(f_i)-min(f_i)leq k-1)的会算重。
所以可以把(k--)再次进行前面的dp。
发现这样子就不会算重了,因为更小的区间([i,i+l])会被计算((k-l+1)-(k-l)=1)
回到原问题。令(f_i)表示(s_{1...i})(a)个数和(b)个数的差,(g_i)表示(s_{1...i})(a)个数和(c)个数的差。
(l1leq f_ileq l1+k,l2leq g_ileq l2+k,l3leq f_i-g_ileq l3+k)
(f_{i,j,k})表示填了(i)个字符,(a_i=l1+j1,b_i=l2+j2)的个数。
在转移时需要满足(l3 leq (l1 + j1) − (l2 + j2) leq l3 + k)
就是(l3-l1+l2leq j1-j2leq l3-l1+l2+k)
枚举(l3-l1+l2)即可。
时间复杂度(O(k^7log_2n))
注意也要容斥。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mo 998244353
int ct,n,k,v1,v2,v3,ans,va;
struct no{
	int a[40][40];
}bz,t;
no operator *(no x,no y){
	no ans=bz;
	for(int i=0;i<ct;i++)
		for(int j=0;j<ct;j++)
			for(int k=0;k<ct;k++)
				ans.a[i][j]=(ans.a[i][j]+x.a[i][k]*y.a[k][j]%mo)%mo;
	return ans;
}
no qp(no x,int y){
	no r=bz;
	for(int i=0;i<ct;i++)
		r.a[i][i]=1;
	for(;y;y>>=1,x=x*x)
		if(y&1)
			r=r*x;
	return r;
}
int pd(int s,int a,int b){
	if(a<0||v2<a||b<0||v3<b||(s-v1>a-b)||(a-b>s))
		return 0;
	return 1;
}
int gt(){
	ct=(v2+1)*(v3+1);
	va=0;
	for(int s=-k;s<=2*k;s++){
		t=bz;
		for(int i=0;i<=v2;i++)
			for(int j=0;j<=v3;j++)
				if(s-v1<=i-j&&i-j<=s){
					int x=i*(v3+1)+j;
					if(pd(s,i+1,j))
						t.a[x][x+v3+1]++;
					if(pd(s,i,j+1))
						t.a[x][x+1]++;
					if(pd(s,i-1,j-1))
						t.a[x][x-v3-2]++;
				}
		no vv=qp(t,n);
		for(int i=0;i<=v1;i++){
			int a=s-i,b;
			if(a<=k&&-k<=a){
				for(int j=0;j<=v2;j++){
					b=j-a;
					if(b>=0&&b<=v3&&i-v1<=j-b-a&&j-b-a<=i)
						for(int l=0;l<ct;l++)
							va=(va+vv.a[j*(v3+1)+b][l])%mo;
				}
			}
		}
	}
	return va;
}
signed main(){
	freopen("baofushehui.in","r",stdin);
	freopen("baofushehui.out","w",stdout);
	scanf("%lld%lld",&n,&k);
	v1=v2=v3=k;
	ans=gt();
	v1--;
	ans=(ans-gt()*3%mo+mo)%mo;
	v2--;
	ans=(ans+gt()*3)%mo;
	v3--;
	ans=(ans-gt()+mo)%mo;
	printf("%lld",ans);
}
原文地址:https://www.cnblogs.com/ctmlpfs/p/14456040.html