省选测试34

总结

基本上就是暴力分,思维还是需要提升

A. 老夫

分析

将所有元素按照 \(b\) 从小到大排序

枚举 \(c\) 的值,将 \(b\) 的值小于 \(c\) 的元素按照 \(a\) 从小到大排序

设一共有 \(m\) 个这样的元素

那么答案就是 \(max((m-i+1) \times a[i])\)

发现每次加入一个新的元素就是把之前所有 \(a\) 的值小于这个元素的 \(i\) 的贡献加上 \(a[i]\)

线段树不大好维护

可以对序列进行分块构建凸包

\(ans=sum+a[i] \times k\)

\(a[i]\) 看成 \(x\),把 \(sum\) 看成 \(y\)

在凸包上二分即可

新加入一个元素时把当前元素所在的凸包暴力重构

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<cmath>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e5+5;
struct jie{
	int vala,valb,rk;
}b[maxn];
bool cmp1(rg jie aa,rg jie bb){
	return aa.valb<bb.valb;
}
bool cmp2(rg jie aa,rg jie bb){
	return aa.vala<bb.vala;
}
int n,w,maxa,maxb,blo,shuyu[maxn],d[maxn],tp,tr[maxn];
inline int lb(rg int xx){
	return xx&-xx;
}
void ad(rg int wz){
	for(rg int i=wz;i<=n;i+=lb(i)) tr[i]++;
}
int cx(rg int wz){
	rg int nans=0;
	for(rg int i=wz;i>0;i-=lb(i)) nans+=tr[i];
	return nans;
}
struct Node{
	int x,id;
	long long y;
	Node(){}
	Node(rg int aa,rg long long bb,rg int cc){
		x=aa,y=bb,id=cc;
	}
	friend bool operator < (const Node& A,const Node& B){
		if(A.x==B.x) return A.y<B.y;
		return A.x<B.x;
	}
}sta[maxn],que[maxn];
std::vector<Node> g[maxn],tmp[maxn];
double getxl(rg Node aa,rg Node bb){
	if(aa.x==bb.x){
		if(bb.y>aa.y) return 1e18;
		else if(bb.y<aa.y) return -1e18;
		else return 0;
	}
	return (double)(bb.y-aa.y)/(double)(bb.x-aa.x);
}
void insert(rg int num){
	rg int id=shuyu[b[num].rk];
	tp=0;
	g[id].clear();
	if(d[id]) for(rg int i=0;i<tmp[id].size();i++) tmp[id][i].y+=1LL*d[id]*tmp[id][i].x;
	d[id]=0;
	ad(b[num].rk);
	tmp[id].push_back(Node(b[num].vala,1LL*b[num].vala*(cx(n)-cx(b[num].rk-1)),b[num].rk));
	for(rg int i=0;i<tmp[id].size();i++) if(tmp[id][i].id<b[num].rk) tmp[id][i].y+=tmp[id][i].x;
	for(rg int i=0;i<tmp[id].size();i++) sta[++tp]=tmp[id][i];
	std::sort(sta+1,sta+tp+1);
	rg int tail=1;
	que[1]=sta[1];
	for(rg int i=2;i<=tp;i++){
		while(tail>1 && getxl(que[tail],sta[i])>=getxl(que[tail-1],que[tail])){
			tail--;
		}
		que[++tail]=sta[i];
	}
	for(rg int i=1;i<=tail;i++) g[id].push_back(que[i]);
	for(rg int i=1;i<id;i++) d[i]++;
}
long long js(){
	rg long long nans=0;
	for(rg int i=1;i<=shuyu[n];i++){
		if(g[i].size()==0) continue;
		rg int l=1,r=g[i].size(),mids;
		while(l<r){
			mids=(l+r)>>1;
			if(getxl(g[i][mids-1],g[i][mids])<=-1.0*d[i]) r=mids;
			else l=mids+1;
		}
		l--;
		nans=std::max(nans,g[i][l].y+1LL*d[i]*g[i][l].x);
	}
	return nans;
}
int main(){
	n=read(),w=read();
	for(rg int i=1;i<=n;i++){
		b[i].vala=read(),b[i].valb=read();
		maxa=std::max(maxa,b[i].vala);
		maxb=std::max(maxb,b[i].valb);
	}
	maxb++;
	std::sort(b+1,b+n+1,cmp2);
	for(rg int i=1;i<=n;i++) b[i].rk=i;
	std::sort(b+1,b+n+1,cmp1);
	blo=sqrt(n);
	for(rg int i=1;i<=n;i++) shuyu[i]=(i-1)/blo+1;
	rg int now=1;
	for(rg int i=1;i<=maxb;i++){
		while(now<=n && b[now].valb<i){
			insert(now);
			now++;
		}
		printf("%lld ",js()+1LL*i*w*(n-now+1));
	}	
	printf("\n");
	return 0;
}

B. 打算

分析

如果按照正常的坐标进行移动,那么在某次操作中,横纵坐标只有一个能够发生变化

不好处理

考虑将坐标 \((x,y)\) 转化成 \((x+y,x-y)\),这样就将两维独立了出来

横纵坐标可以任意加 \(1\)\(1\),并且和原来的坐标变换一一对应

\(s_i\) 表示 \(i\) 时刻的位置

\(s_i=s_{t_i \ mod \ L}+s_{L}*\lfloor \frac{t_i}{L} \rfloor\)

可以写成 \(s_{t_i \ mod \ L}=As_L+B\) 的形式

按照 \(s\) 的 下标排序,把相邻两个等式之间作差

根据时间和路程的关系 \(|s_i-s_{i-1}| \leq t_i-t_{i-1}\),可以解得 \(s_L\) 的范围

回代即可求出其它的值

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define rg register
const int maxn=2e6+5;
const long double orz=1.0;
int n,l;
long long t[maxn],x[maxn],y[maxn];
struct Node{
	long long a,b,t;
	Node(){}
	Node(rg long long aa,rg long long bb,rg long long cc){
		a=aa,b=bb,t=cc;
	}
	friend bool operator < (const Node& A,const Node& B){
		return A.t<B.t;
	}
}jlx[maxn],jly[maxn];
void bye(){
	printf("NO\n");
	std::exit(0);
}
int ansx[maxn],ansy[maxn];
void solve(rg Node A[],rg long long &L,rg long long &R,rg int ans[]){
	L=-1e18,R=1e18;
	for(rg int i=2;i<=n;i++){
		rg long long nl=A[i-1].t-A[i].t,nr=A[i].t-A[i-1].t,na=A[i].a-A[i-1].a,nb=A[i].b-A[i-1].b;
		if(na==0){
			if(nb<nl || nb>nr) bye();
		} else if(na<0){
			L=std::max(L,(long long)std::ceil(orz*(nr-nb)/na));
			R=std::min(R,(long long)std::floor(orz*(nl-nb)/na));
		} else {
			L=std::max(L,(long long)std::ceil(orz*(nl-nb)/na));
			R=std::min(R,(long long)std::floor(orz*(nr-nb)/na));
		}
	}
	if((L+l)&1LL) L++;
	if((R+l)&1LL) R--;
	if(L>R) bye();
	A[1].b+=L*A[1].t;
	for(rg int i=2;i<=n;i++){
		A[i].b+=L*A[i].a;
		if(A[i].t==A[i-1].t) continue;
		rg long long tmp=A[i-1].t,cz=A[i].b-A[i-1].b;
		while(cz>0){
			ans[tmp]=1;
			tmp++;
			cz--;
		}
		while(cz<0){
			ans[tmp]=0;
			tmp++;
			cz++;
		}
		while(tmp+2<=A[i].t){
			ans[tmp]=1;
			tmp++;
			ans[tmp]=0;
			tmp++;
		}
		if(tmp!=A[i].t) bye();
	}
}
int main(){
	scanf("%d%d",&n,&l);
	rg long long tmpx,tmpy;
	for(rg int i=1;i<=n;i++){
		scanf("%lld%lld%lld",&t[i],&x[i],&y[i]);
		if((t[i]&1LL)!=((x[i]+y[i])&1LL)) bye();
		tmpx=x[i]+y[i];
		tmpy=x[i]-y[i];
		x[i]=tmpx,y[i]=tmpy;
	}
	for(rg int i=1;i<=n;i++){
		jlx[i]=Node(-1LL*t[i]/l,x[i],t[i]%l);
		jly[i]=Node(-1LL*t[i]/l,y[i],t[i]%l);
	}
	jlx[++n]=Node(0,0,0),jly[++n]=Node(0,0,0);
	jlx[++n]=Node(1,0,l),jly[++n]=Node(1,0,l);
	std::sort(jlx+1,jlx+1+n),std::sort(jly+1,jly+1+n);
	rg long long lx,rx,ly,ry;
	solve(jlx,lx,rx,ansx);
	solve(jly,ly,ry,ansy);
	for(rg int i=0;i<l;i++){
		if(ansx[i] && ansy[i]) printf("R");
		else if(ansx[i] && !ansy[i]) printf("U");
		else if(!ansx[i] && ansy[i]) printf("D");
		else printf("L");
	}
	printf("\n");
	return 0;
}

C. 报复社会

分析

考虑字符集只有 \(2\) 的情况

设这两种字符分别为 \(1,2\)

\(g_i=sum1_i-sum2_i\),则要求对于任意 \(i,j \in [0,n]\),\(g_i\)\(g_j\) 的差值不超过 \(k\)

因为 \(g_0=0\),所以合法的区间为 \([-k,0],[-k+1,1] \cdots [0,k]\)

然而这样会使一些方案算重,也就是 \([−k+1,0]\) 等区间被算了两次,所以要减掉一次

对于长度更小的区间 \([i,i+l]\) ,会被计算 \((k-l+1)-(k-l)=1\) 次,恰好被容斥掉了,所以不用考虑

每一次转移的系数是一样的,所以可以用矩阵快速幂优化

考虑扩展到字符集为 \(3\) 的情况

\(g1_i=cnt1_i-cnt3_i,g2_i=cnt2_i-cnt3_i,g3_i=cnt1_i-cnt3_i\)

\(g_1\) 合法的区间为 \([l_1,l_1+d_1]\),\(g_2\) 合法的区间为 \([l_2,l_2+d_2]\),\(g_3\) 合法的区间为 \([l_3,l_3+d_3]\)

\(f[o][i][j]\) 为当前考虑到第 \(o\) 个位置,\(g1\) 和最小值的差为 \(i\)\(g2\) 和最小值的差为 \(j\) 的方案数

也就是把 \(i\) 看作 \(l_1+i\),把 \(j\) 看作 \(l_2+j\)

\(g3\) 可以由 \(g1\)\(g2\) 推得

为了使 \(g_3\) 的范围合法,必须满足 \(l_3 \leq l_1+i-(l_2+j) \leq l_3+d_3\)

剩下的就和字符集为 \(2\) 的一样了

拿子集反演容斥,奇减偶加

一共有 \(2^3\) 种情况,但是本质不同的情况只有 \(4\)

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define rg register
const int maxn=39,mod=998244353;
inline int addmod(rg int now1,rg int now2){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
	return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
	return now1*=now2,now1>=mod?now1%mod:now1;
}
long long n;
int m,cnt,a[maxn],b[maxn][maxn];
void mul1(){
	int tmp[maxn][maxn];
	memset(tmp,0,sizeof(tmp));
	for(rg int i=1;i<=cnt;i++){
		for(rg int j=1;j<=cnt;j++){
			for(rg int k=1;k<=cnt;k++){
				tmp[i][j]=addmod(tmp[i][j],mulmod(b[i][k],b[k][j]));
			}
		}
	}
	memcpy(b,tmp,sizeof(tmp));
}
void mul2(){
	int tmp[maxn];
	memset(tmp,0,sizeof(tmp));
	for(rg int i=1;i<=cnt;i++){
		for(rg int j=1;j<=cnt;j++){
			tmp[i]=addmod(tmp[i],mulmod(a[j],b[j][i]));
		}
	}
	memcpy(a,tmp,sizeof(tmp));
}
int id[maxn][maxn],ans;
int solve(rg int l1,rg int d1,rg int l2,rg int d2,rg int l3,rg int d3){
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(id,0,sizeof(id));
	cnt=0;
	for(rg int i=0;i<=d1;i++){
		for(rg int j=0;j<=d2;j++){
			rg int tmp=(l1+i)-(l2+j);
			if(tmp>=l3 && tmp<=l3+d3) id[i][j]=++cnt;
		}
	}
	for(rg int i=0;i<=d1;i++){
		for(rg int j=0;j<=d2;j++){
			if(id[i][j]){
				if(id[i+1][j]) b[id[i][j]][id[i+1][j]]++;
				if(id[i][j+1]) b[id[i][j]][id[i][j+1]]++;
				if(i && j && id[i-1][j-1]) b[id[i][j]][id[i-1][j-1]]++;
			}
		}
	}
	a[id[-l1][-l2]]=1;
	rg long long tmp=n;
	while(tmp){
		if(tmp&1LL) mul2();
		mul1();
		tmp>>=1LL;
	}
	rg int nans=0;
	for(rg int i=1;i<=cnt;i++) nans=addmod(nans,a[i]);
	return nans;
}
int main(){
	scanf("%lld%d",&n,&m);
	for(rg int i=-m;i<=0;i++){
		for(rg int j=-m;j<=0;j++){
			for(rg int k=-m;k<=0;k++){
				ans=addmod(ans,solve(i,m,j,m,k,m));
			}
		}
	}
	for(rg int i=-m;i<=0;i++){
		for(rg int j=-m;j<=0;j++){
			for(rg int k=-m+1;k<=0;k++){
				ans=delmod(ans,mulmod(3,solve(i,m,j,m,k,m-1)));
			}
		}
	}
	for(rg int i=-m;i<=0;i++){
		for(rg int j=-m+1;j<=0;j++){
			for(rg int k=-m+1;k<=0;k++){
				ans=addmod(ans,mulmod(3,solve(i,m,j,m-1,k,m-1)));
			}
		}
	}
	for(rg int i=-m+1;i<=0;i++){
		for(rg int j=-m+1;j<=0;j++){
			for(rg int k=-m+1;k<=0;k++){
				ans=delmod(ans,solve(i,m-1,j,m-1,k,m-1));
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-03-02 21:09  liuchanglc  阅读(80)  评论(0编辑  收藏  举报