agc030 vp记录

T1签到题。


  • [AGC030B] Tree Burning

高桥湖是周长为 L 的一个首尾相接的圆,圆上整点标为0,1,2,...,L1.

在湖边有 N 颗树,分别在距离起点顺时针数 X1,X2,...,Xn 的位置上。保证位置 0 没有树。

高桥君初始在位置 0 上,每次可以选择顺时针或逆时针走到第一颗还没有被点燃过的树并将其点燃,直到所有树都被点燃为止。

求高桥君最长的移动距离。

 
有一个贪心就是你可以先一直向某一个方向走,而后在顺逆时针交叉走。
跑个前后缀再枚举一下最开始的连续段即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=200010;
int n,m,sum[N],pre[N][2],bac[N][2],ans;
signed main(){
	m=rd(),n=rd(),sum[n+1]=m;
	for(int i=1;i<=n;i++)sum[i]=rd();
	for(int i=1;i<=n;i++)pre[i][0]=pre[i-1][0]+sum[i]-sum[i-1];
	for(int i=1;i<=n;i++)pre[i][1]=pre[i-1][1]+pre[i][0];
	for(int i=n;i>=1;i--)bac[i][0]=bac[i+1][0]+sum[i+1]-sum[i];
	for(int i=n;i>=1;i--)bac[i][1]=bac[i][0]+bac[i+1][1];
	for(int i=1;i<=n;i++){
		int ansn=pre[i][0]*(n-i+1),mid=n-(n-i)/2+1;
		if(mid<=n)ansn+=bac[mid][1]*2;
		if(mid-2>i)ansn+=(pre[mid-2][1]-pre[i][1]-pre[i][0]*(mid-2-i))*2;
		if(i<n){
			if((n-i)&1)ansn+=bac[mid-1][0];
			else ansn+=pre[mid-1][0]-pre[i][0];
		}
		ans=max(ans,ansn);
	}
	for(int i=n;i>=1;i--){
		int ansn=bac[i][0]*i,mid=(i-1)/2+1;
//		cout<<i<<":("<<mid<<"):"<<ansn<<"\n";
		if(mid>1)ansn+=pre[mid-1][1]*2;
//		cout<<i<<":("<<mid<<"):"<<ansn<<"\n";
		if(mid+1<i)ansn+=(bac[mid+1][1]-bac[i][1]-bac[i][0]*(i-mid-1))*2;
//		cout<<i<<":("<<mid<<"):"<<ansn<<"\n";
		if(i>1){
			if((i-1)&1)ansn+=pre[mid][0];
			else ansn+=bac[mid][0]-bac[i][0];
		}
//		cout<<i<<":("<<mid<<"):"<<ansn<<"\n";
		ans=max(ans,ansn);
	}
	printf("%lld\n",ans);
	return 0;
}

  • [AGC030C] Coloring Torus

  • 给定一个数字 K

  • 你需要构造一个 n×n 的矩阵 A,需要满足以下条件:

    • n[1,500]
    • i,j[1,n],Ai,j[1,K] 且为整数。
    • v[1,K],i,j[1,n],Ai,j=v
    • cnti,j,v 表示 Aimodn+1,j,Ai,jmodn+1,A(i2+mod)modn+1,j,Ai,(j2+mod)modn+1 四个数中等于 v 的数的个数。那么对于矩阵中任意两个数 Ai,jAx,y,若 Ai,j=Ax,y,则需要满足 v[1,K],cnti,j,v=cntx,y,v
  • 输出任意一个合法的方案即可。

  • 1K103

 
如果读懂了题面那么就是挺典的一道题?CF好像出过一模一样的。
如果 k<=500,直接每一行填一种。
如果不是,可以对角线上填,一条对角线可以交替填两种不同的数字。
贴张别人的图。
image

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=510;
int nx[N],A[N][N];
signed main(){
	int k=rd();
	if(k<=500) {
		printf("%d\n",k);
		for(int i=1;i<=k;i++)for(int j=1;j<=k;j++)printf("%d%c",i," \n"[j==k]);
	}else{
		int n=500,N=0;
		for(int i=1;i<=n;i++)nx[i]=i+1;
		nx[n]=1,printf("500\n"),k-=n;
		for(int i=1;i<=n;i++){
			int a=++N,b=k?++N:N;
			k-=b-a;
			for(int x=1,y=i;x<=n;x++,y=nx[y])A[x][y]=x&1?a:b;
		}
		for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)printf("%d%c",A[i][j]," \n"[j==n]);
	}
	return 0;
}

  • [AGC030D] Inversion Sum

给你一个长度为 n 的数列,然后给你 q 个交换或不交换操作,你可以选择操作或者不操作,问所有情况下逆序对的总和。
 
经典能否操作,DP转概率的题。
fi,j表示 ij 大的概率,每次操作只会影响 O(n) 个位置,最后所有可能的概率加起来乘个 2m

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=3010,mod=1000000007;
int f[N][N],sum[N],n,m,inv,ans;
inline int pw(int x,int y){
	int ansn=1;
	while(y){
		if(y&1)ansn=ansn*x%mod;
		x=x*x%mod,y/=2;
	}
	return ansn;
}
signed main(){
	n=rd(),m=rd();
	inv=pw(2,mod-2);
	for(int i=1;i<=n;i++)sum[i]=rd();
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j]=(sum[i]>sum[j]);
	for(int i=1;i<=m;i++){
		int x=rd(),y=rd();
		for(int j=1;j<=n;j++){
			if(j==x||j==y)continue;
			int a=f[j][x],c=f[x][j],b=f[j][y],d=f[y][j];
			f[j][x]=(a+b)*inv%mod;
			f[j][y]=(a+b)*inv%mod;
			f[x][j]=(c+d)*inv%mod;
			f[y][j]=(c+d)*inv%mod;
		}
		f[x][y]=f[y][x]=(f[x][y]+f[y][x])*inv%mod; 
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			ans+=f[i][j],ans%=mod;
		}
	}
	for(int i=1;i<=m;i++)ans=ans*2%mod;
	printf("%lld\n",ans);
	return 0;
}

  • [AGC030E] Less than 3

给定两个长度为 n01s,t , 串的连续段长度不超过 2 , 每次操作可以把 s 的一个位置反转, 要求操作后连续段长度仍不超过 2 , 求使得 s,t 相等的最少操作次数.
 

考虑在 0 与 1 间画一条红线,1 与 0 间画一条蓝线,并且假设开头和结尾都有无线交替的红蓝线,那么两个串相等等价于它们对应的红蓝线相等。每次可以移动一条红线或蓝线,要保证移动后红蓝线交替出现且相邻的红蓝线距离为 1 或 2,具体如下图。image
假设我们已经确定了s中的红线和t中的蓝线的对应关系,显然答案的下界是对应线段的距离之和。
image
这个下界也是能达到的,因为要求相邻线之间的距离不超过 2,所以向右移的线和向左移动的线不能相邻,那么就相当于保持不动的线将所有线分成了若干部分,每一部分的线都是向同一个方向移动,显然在每一部分内一定存在一种移动方案。

枚举对应方案计算答案即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=5010;
int n,cnt[2],ans=INT_MAX;
char s[N];//1->0:0  0->1:1
struct node{
	int p,t;
}que[2][N];
signed main(){
	n=rd();
	for(int k=0;k<=1;k++){
		scanf("%s",s+1);
		if(s[1]=='0')que[k][++cnt[k]]=(node){0,0};
		else que[k][++cnt[k]]=(node){0,1};
		for(int i=2;i<=n;i++){
			if(s[i]=='0'&&s[i-1]=='1')que[k][++cnt[k]]=(node){i-1,0};
			else if(s[i]=='1'&&s[i-1]=='0')que[k][++cnt[k]]=(node){i-1,1};
		}
	}
	for(int i=1;i<=cnt[0];i++){
		if(que[0][i].t!=que[1][1].t)continue;
		int ansn=0;
		for(int j=1;j<i;j++)ansn+=que[0][j].p;
		int l=i,r=1;
		while(l<=cnt[0]&&r<=cnt[1])ansn+=abs(que[0][l].p-que[1][r].p),l++,r++;
		while(l<=cnt[0])ansn+=n-que[0][l].p,l++;
		while(r<=cnt[1])ansn+=n-que[1][r].p,r++;
		ans=min(ans,ansn); 
	}
	for(int i=1;i<=cnt[1];i++){
		if(que[1][i].t!=que[0][1].t)continue;
		int ansn=0;
		for(int j=1;j<i;j++)ansn+=que[1][j].p;
//		cout<<i<<":"<<ansn<<"\n";
		int l=i,r=1;
		while(l<=cnt[1]&&r<=cnt[0])ansn+=abs(que[1][l].p-que[0][r].p),l++,r++;
		while(l<=cnt[1])ansn+=n-que[1][l].p,l++;
		while(r<=cnt[0])ansn+=n-que[0][r].p,r++;
		ans=min(ans,ansn); 
	}
	if(ans==INT_MAX)ans=n;
	printf("%d\n",ans);
	return 0;
}

  • [AGC030F] Permutation and Minimum

有一个 2N 个数的序列 A,从 12N 标号。你要把 12N 这些数填进去,使它形成一个排列。

但是已经有一些位置强制填了特定的数了,输入时会给出。

最后令长度为 N 的序列 B 为:令 Bi=min{A2i1,A2i}

询问所有方案中能得到的不同的 B 的数量。

  • 1N300

 
一道好的DP转化题。
既然 Bi 以数对中较小的一个数决定,那么我们就要用大的数去匹配小的数。
考虑将数字两两配对,相当于在数轴上联一条线段。
如果 A2i1,A2i都确定,那么此对对答案没有贡献,先排除掉。
如果线段端点只有一个位置是确定的,那么另一个数确定它的值。
如果两个线段端点都没确定,那么所有这样的对是随便填的,答案乘上对数量的阶乘。
从大向小考虑,fi,j,k表示现在已经处理完 i 到 n*2 的数,有 j 个未确定位置的数需要匹配,有 k 个未确定位置的数。
已确定位置的数相互之间不能匹配,其余随便匹配。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=610,mod=1000000007;
int n,sum[N],v[N],f[3][N][N],ans,cnt;
inline void ad(int &x,int y){x=x+y,x=(x>=mod)?x-mod:x;}
signed main(){
	n=rd()*2,cnt=n/2;
	for(int i=1;i<=n;i++)sum[i]=rd();
	for(int i=1;i<=n;i++){
		if((i&1)&&(sum[i]!=-1&&sum[i+1]!=-1))v[sum[i]]=v[sum[i+1]]=-1,i++,cnt--;
		else if(sum[i]!=-1)v[sum[i]]=1,cnt--;
	}
	int t=0;
	f[t][0][0]=1;
	for(int i=n+1;i>=2;i--){
		for(int j=0;j<=n;j++)for(int k=0;k<=n;k++)f[t^1][j][k]=0;
		if(v[i-1]==-1){
			for(int j=0;j<=n;j++){
				for(int k=0;k<=n;k++)f[t^1][j][k]=f[t][j][k];
			}
			continue;
		}
		for(int j=0;j<=n;j++){
			for(int k=0;k<=n;k++){
				if(!f[t][j][k])continue;
				if(v[i-1]==0){
					ad(f[t^1][j+1][k],f[t][j][k]);
					if(j>0)ad(f[t^1][j-1][k],f[t][j][k]);
					if(k>0)ad(f[t^1][j][k-1],1ll*f[t][j][k]*k%mod);
				}
				else{
					if(j>0)ad(f[t^1][j-1][k],f[t][j][k]);
					ad(f[t^1][j][k+1],f[t][j][k]);
				}
			}
		}
		t^=1;
	}
	ans=f[t][0][0];
	while(cnt>0)ans=1ll*ans*cnt%mod,cnt--;
	printf("%d\n",ans);
	return 0;
}
posted @   flywatre  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示