「EZEC」 Round 10 庆典 div3

「EZEC」 Round 10 庆典 div3


「EZEC-10」打分(题目

题目大意:给出一个序列 a 1 , a 2 . . . a n a_1,a_2...a_n a1,a2...an,可以操作 m m m次,每次使 a i + 1 ( 1 ⩽ i ⩽ n ) a_i+1(1\leqslant i \leqslant n) ai+1(1in),问去掉一个最大值一个最小值之后和的最大值

分析:

  1. 我们可以将原序列排序,因为可以去掉最小值,所以说 a 1 a_1 a1我们可以不管,剩下就是要操作 m m m次,使得去掉最大值后的和尽可能大
  2. 那么我们其实就要使得 a 2 − a n − 1 a_2-a_{n-1} a2an1尽可能大,但 不 能 大 于 a n 不能大于a_n an,不然 a n a_n an就不是被舍掉的了,如果每个数都与 a n a_n an相等了,但 m m m还没操作完,就可以将 a n + 1 a_n+1 an+1这样就有了更大的上限,重复操作到 m m m次即可
  3. 其实可以把这些操作转换成算式的,如果没有使每个都等于 a n a_n an,那么就是将 a 2 . . . a n − 1 a_2...a_{n-1} a2...an1加了 m m m,那平均数就是 ( ( ∑ i = 2 n − 1 a i ) + m ) ((\sum_{i=2}^{n-1}a_i)+m) ((i=2n1ai)+m),如果不是,那么就是先将每个数补到 m m m,需要用 t t t次,那就剩下 m − t m-t mt没操作,现在和已经是 a n ∗ ( n − 2 ) a_n*(n-2) an(n2)了,所以在 a n a_n an的基础上 + ( m − t − 1 ) / ( n − 2 + 1 ) + ( m − t − 1 ) / +(m-t-1)/(n-2+1)+(m-t-1)/%(n-2+1) +(mt1)/(n2+1)+(mt1)/将剩下的 m − t m-t mt次操作平均分配到 a 2 . . . a n a_2...a_n a2...an里,记得有余数。

代码实现:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
typedef long long ll;

void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int n,m;
int mmax=0,mmin=1<<30;
int a[100010],sum;
signed main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	read(n);read(m);
	for (int i=1;i<=n;i++) read(a[i]),mmin=min(mmin,a[i]),mmax=max(mmax,a[i]),sum+=a[i];
	sum-=mmax+mmin;
	if (mmax*(n-2)-sum>=m) printf("%lld",(sum+m));
	else m-=mmax*(n-2)-sum,sum=mmax*(n-2)+(m-1)/(n-1)*(n-2)+(m-1)%(n-1),printf("%lld",sum);
//	fclose(stdin);fclose(stdout);
	return 0;
}


「EZEC-10」排列排序(题目

题目大意:给出 a 1 . . . a n a_1...a_n a1...an,每次可以将一个区间 [ l , r ] [l,r] [l,r]排序,花费 r − l + 1 r-l+1 rl+1的代价,问使得 a a a有序的最少代价

分析:

1.我们发现如果要让 a i a_i ai回到 a a i a_{a_i} aai的位置,那么至少要花费 ∣ a i − a a i + 1 ∣ |a_i-a_{a_i}+1| aiaai+1的代价,那么其实就是每次将 m i n ( a i , a a i ) min(a_i,a_{a_i}) min(ai,aai)~ m a x ( a i , a a i ) + 1 max(a_i,a_{a_i})+1 max(ai,aai)+1,之后的需要操作的区间总长就是答案了(即 > 0 >0 >0的位置)
2. 区间 + 1 +1 +1操作就要差分来维护,你要线段树也可以,YBH线段树

代码实现:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;

void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int t;
int n,a[1000100],b[1000100],c[1000100];
int main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	read(t);
	while (t--)
	{
		read(n);
		for (int i=1;i<=n;i++)
			read(a[i]),b[i]=0;
		for (int i=1;i<=n;i++)
			if (i<a[i])
				b[i]+=1,b[a[i]+1]-=1; 
		int ans=0;
		for (int i=1;i<=n;i++)
			c[i]=c[i-1]+b[i];
		for (int i=1;i<=n;i++)
			if (c[i]>0) ans++;
		printf("%d\n",ans);
	}
//	fclose(stdin);fclose(stdout);
	return 0;
}


「EZEC-10」Shape(题目

题目大意:如图所示

分析:

  1. 为了方便,我将白色点设为 1 1 1,黑色点设为 0 0 0
  2. 在设一个数组 l r [ i ] [ j ] lr[i][j] lr[i][j]表示第 i i i行第 j j j个想左边最多有多少个连续的白色(包括自己),在设一个数组 u d [ i ] [ j ] ud[i][j] ud[i][j]表示第 i i i行第 j j j个可以向上下同时延申多少格,这两个数组都可以用前缀和 O ( n ∗ m ) O(n*m) O(nm)维护
  3. 通过 l r [ i ] [ j ] lr[i][j] lr[i][j]我们可以知道每条横着的连续白点的位置,在通过 u d [ i ] [ j ] ud[i][j] ud[i][j]我们就可以得到这样一幅图图片这就是这些白点的一个连通块,将它们排序按 u p up up,并不会影响它们之间组成 H H H形的数量,对于 u p [ i ] [ j ] up[i][j] up[i][j]的来说他能与每一个 u p [ k ] [ l ] up[k][l] up[k][l]大于它的构成 u p [ i ] [ j ] up[i][j] up[i][j]个,因为与顺序无关,所以只用算比他大的个数乘以 u p [ i ] [ j ] up[i][j] up[i][j]就可以了,时间复杂度是 O ( n ∗ m ∗ log ⁡ 2 m ) O(n*m*\log_2m) O(nmlog2m)

代码实现:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
long long ans=0;
int n,m;
int mid_up_max[2*1010][2*1010],mid_down_max[2*1010][2*1010];
int up_down_min[2*1010][2*1010];
int left_right_max[2*1010][2*1010],up_down_min_sum[2*1010][2*1010];
int a[2*1010][2*1010]; 
int main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	read(n),read(m);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			read(a[i][j]),a[i][j]=!a[i][j];
	for (int i=1;i<=n;i++)
	{
		left_right_max[i][m+1]=-1;
		for (int j=m;j>=1;j--)
			if (a[i][j]==1)
				left_right_max[i][j]=left_right_max[i][j+1]+1;
			else 
				left_right_max[i][j]=-1;
	}
	for (int j=1;j<=m;j++)
	{
		mid_up_max[0][j]=-1;
		for (int i=1;i<=n;i++)
			if (a[i][j]==1)
				mid_up_max[i][j]=mid_up_max[i-1][j]+1;
			else
				mid_up_max[i][j]=-1;
	}
	for (int j=1;j<=m;j++)
	{
		mid_down_max[n+1][j]=-1;
		for (int i=n;i>=1;i--)
			if (a[i][j]==1)
				mid_down_max[i][j]=mid_down_max[i+1][j]+1;
			else
				mid_down_max[i][j]=-1;
	}
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			up_down_min[i][j]=min(mid_up_max[i][j],mid_down_max[i][j]);
	/*
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)
			printf("%d ",left_right_max[i][j]);
		printf("\n");
	}
	printf("\n");
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)
			printf("%d ",up_down_min[i][j]);
		printf("\n");
	}
	printf("\n");
	*/
	/*
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			if (up_down_min[i][j]==-1)
				up_down_min_sum[i][j]=-1;
			else if (up_down_min[i][j-1]==-1)
				up_down_min_sum[i][j]=up_down_min[i][j];
			else up_down_min_sum[i][j]=up_down_min_sum[i][j-1]+up_down_min[i][j];
	
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)
			printf("%d ",up_down_min_sum[i][j]);
		printf("\n");
	}
	printf("\n");
	*/
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			if (a[i][j]==1)
			{
				int s[2*1010],len=0;
				for (int k=j;k<=j+left_right_max[i][j];k++)
					s[++len]=up_down_min[i][k];
				sort(s+1,s+len+1);
				for (int k=1;k<=len;k++)
					ans+=(len-k)*s[k];
				j=j+left_right_max[i][j];
			}
	printf("%lld\n",ans);
//	fclose(stdin);fclose(stdout);
	return 0;
}


「EZEC-10」Covering(题目

题目大意:有 k k k张长度为 1 ∗ 2 1*2 12的纸片,从中选出 l l l~ r r r个,按从编号小到编号大的顺序放入 n ∗ m n*m nm的方格中,后放入的会覆盖前面的,使得最后的方格如给出的 n ∗ m n*m nm的方格一样,问有几种放法

分析:

  1. 分三步走,令 a n s = 1 ans=1 ans=1
  2. 先将编号在棋盘中出现两次的方格去掉,它们不会影响答案
  3. 找出出现过一次的纸片,对于每一张,它都会有上下左右编号大于它的纸片的总和的放法, a n s ∗ 上 下 左 右 大 于 它 的 总 和 数 ans*上下左右大于它的总和数 ans
  4. 对于没有出现过的,我们设 f [ i ] [ j ] f[i][j] f[i][j]表示 1 1 1~ i i i张纸条中,选出 j j j个的放置方案总数,则我们要求的就是 f [ 没 有 出 现 过 的 总 数 ] [ m a x ( 0 , l − 出 现 过 的 总 数 ) ] + . . . + f [ 没 有 出 现 过 的 总 数 ] [ r − 出 现 过 的 总 数 ] f[没有出现过的总数][max(0,l-出现过的总数)]+...+f[没有出现过的总数][r-出现过的总数] f[][max(0,l)]+...+f[][r],最后将它乘上 a n s ans ans就好了,这个东西可以用 D P DP DP来求,转移式 f [ i ] [ j ] = i 的 放 法 ∗ f [ i − 1 ] [ j ] + f [ i ] [ j − 1 ] f[i][j]=i的放法*f[i-1][j]+f[i][j-1] f[i][j]=if[i1][j]+f[i][j1]

代码实现:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
#define mod 1000000007
using namespace std;
typedef long long ll;

void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
int t;
int n,m,k,l,r;
struct point { int cnt,x[2],y[2]; }p[1010];
int v[1010][1010],vl,a[1010][1010];
int f[1010][1010];
int num[1010],ans,len,bis;
int fx[4]={1,0,-1,0};
int fy[4]={0,1,0,-1};
int calc(int x,int y)
{
	int g=0;
	for (int t=0;t<=3;t++)
	{
		int tx=x+fx[t],ty=y+fy[t];	
		if (a[tx][ty]>a[x][y]) g++;
	}	
	return g;
}
void down(int x,int y)
{
	for (int t=0;t<=3;t++)
	{
		int tx=x+fx[t],ty=y+fy[t];	
		vl+=v[tx][ty];vl%=mod;
	}
	v[x][y]=1;
}
int max(int x,int y) { if (x>y) return x; else return y; }
signed main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	read(t);
	while (t--)
	{
		memset(f,0,sizeof(f));
		memset(num,0,sizeof(num));ans=len=0;bis=1;
		memset(p,0,sizeof(p));memset(v,0,sizeof(v));
		memset(a,0,sizeof(a));
		read(n),read(m),read(k);read(l),read(r);vl=0;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++)
			{
				read(a[i][j]);
				if (a[i][j]!=0)
					p[a[i][j]].x[p[a[i][j]].cnt]=i,p[a[i][j]].y[p[a[i][j]].cnt]=j,p[a[i][j]].cnt++;
			}	
		for (int i=k;i>=1;i--)
		{
			if (p[i].cnt==0) num[++len]=vl%mod;
			else if (p[i].cnt==1) bis=bis*calc(p[i].x[0],p[i].y[0])%mod,down(p[i].x[0],p[i].y[0]);
			else down(p[i].x[0],p[i].y[0]),down(p[i].x[1],p[i].y[1]);
		}
		f[len][0]=1;
		for (int i=1;i<=len;i++) f[i][1]=(f[i-1][1]+num[i])%mod;
		for (int i=2;i<=len;i++)
			for (int j=2;j<=i;j++)
				f[i][j]=((f[i-1][j-1]*num[i]%mod)+f[i-1][j])%mod;
//		printf("%lld %lld %lld\n",l,k,len);
		for (int i=max(0,l-k+len);i<=r-k+len;i++)
			ans=(f[len][i]+ans)%mod;
//		printf("%lld %lld\n",ans,bis);
		printf("%lld\n",bis*ans%mod);
	}
//	fclose(stdin);fclose(stdout);
	return 0;
}


「EZEC-10」序列(题目

题目大意:如题

分析:

  1. 根据给出的关系式,将每个有关联的连一条边,边权为 z i z_i zi
    2, 这样我们就得到了许多个联通块,每个连通块之间互补干扰,我们单独讨论一个连通块
  2. 我们可以从一个点出发,遍历到每一个点,根据异或的结合律,我们就可以用起点表示出连通块中的每一个点,如果一个点有不同的表达方式,则无解
  3. 这样我们就要找到一个值,令起点为这个值,使得连通块上的每一点都小于 k k k,其实问的就是一个异或的最大值,容易想到用 t r i e trie trie树来维护
  4. 假设这个连通块的点为 a 1 , a 2 . . a n a_1,a_2..a_n a1,a2..an,令 a 2 = a 1 ⊗ g 1 , . . . a n = a 1 ⊗ g n a_2=a_1 \otimes g_1,...a_n=a_1 \otimes g_n a2=a1g1,...an=a1gn,将 g 1 . . g n g_1..g_n g1..gn插入树中,然后从根节点开始遍历, x , d , s u m x,d,sum x,d,sum表示当前节点的编号, d d d表示当前在第几位, s u m sum sum表示当前的异或值
  5. ①如果 x x x没有儿子,证明已经匹配完了,如果此时的 s u m ⩽ k sum \leqslant k sumk,说明是可行的返回 1 1 1,否则返回 0 0 0
    ②如果有两个儿子,那无论这一位是什么,都可以异或到 1 1 1,所以 s u m + 2 d sum+2^d sum+2d d f s 01 dfs01 dfs01儿子
    ③如果有一个儿子,我们设它为 0 0 0儿子,( 1 1 1儿子同理)如果 s u m + 2 d ⩽ k sum+2^d\leqslant k sum+2dk说明这一位取 0 0 0 时,异或值为 0 0 0,肯定都是可以的,可以返回的答案 + 2 d +2^d +2d,再将 s u m + 2 d sum+2^d sum+2d d f s dfs dfs 1 1 1儿子
  6. 最后所有返回值相乘就是答案

代码实现:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mod 1000000007
#define int long long
using namespace std;

void read(int &sum)
{
	sum=0;char last='w',ch=getchar();
	while (ch<'0' || ch>'9') last=ch,ch=getchar();
	while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
	if (last=='-') sum=-sum;
}
struct trie
{
	int ch[31*5*100010][2];
	int tot;
	void init()
	{
		for (int i=1;i<=tot;i++) ch[i][0]=ch[i][1]=0;
		tot=1;
	}
	void add(int x)
	{
		int now=1;
		for (int i=30;i>=0;i--)
		{
			int t=(x>>i)&1;//printf("%d %d\n",now,t);
			if (ch[now][t]==0)
			{
				tot++;
				ch[now][t]=tot;
			}
			now=ch[now][t];
		}
	}
}tr;
int n,m,k,ans=1;
struct node { int i,j,x; }p[5*100010];
struct edge { int x,y,next,c; }a[5*2*100010];
int len,last[5*100010];
int bz[5*100010],re,vis[5*100010];
void ins(int x,int y,int c)
{
	len++,a[len].x=x,a[len].y=y,a[len].c=c;
	a[len].next=last[x],last[x]=len;
}
void wwww(int x,int fa)
{
	if (re==1) return ;
	tr.add(bz[x]);
	vis[x]=1;
	for (int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if (y!=fa)
		{
			if (bz[y]!=-1 && (bz[x]^a[k].c)!=bz[y]) { re=1; return ; }
			bz[y]=bz[x]^a[k].c;
			if (vis[y]==-1) wwww(y,x);			
		}
	}
}
int dfs(int x,int d,int sum)
{
	if (sum>k) return 0;
	if (tr.ch[x][0]==0 && tr.ch[x][1]==0) return sum<=k;
	if (tr.ch[x][0]>0 && tr.ch[x][1]>0)
	{
		sum+=(1<<d);
		return dfs(tr.ch[x][0],d-1,sum)+dfs(tr.ch[x][1],d-1,sum);
	}
	if (tr.ch[x][0]>0)
	{
		if (sum+(1<<d)<=k) return (1<<d)+dfs(tr.ch[x][0],d-1,sum+(1<<d));
		else return dfs(tr.ch[x][0],d-1,sum);
	}
	if (tr.ch[x][1]>0)
	{
		if (sum+(1<<d)<=k) return (1<<d)+dfs(tr.ch[x][1],d-1,sum+(1<<d));
		else return dfs(tr.ch[x][1],d-1,sum);
	}
}
signed main()
{
//	freopen("M.in","r",stdin);
//	freopen("M.out","w",stdout);
	read(n),read(m),read(k);
	for (int i=1;i<=n;i++) bz[i]=-1,vis[i]=-1;
	for (int i=1;i<=m;i++)
	{
		read(p[i].i),read(p[i].j),read(p[i].x);
		ins(p[i].i,p[i].j,p[i].x),ins(p[i].j,p[i].i,p[i].x);
	}
	for (int i=1;i<=n;i++)
	{
		if (bz[i]==-1)
		{
			if (last[i]==0) ans=ans*(k+1)%mod;
			else
			{
				tr.init();
				re=0;
				bz[i]=0,wwww(i,-1);
				if (re==1) { printf("0"); return 0; }
				ans=ans*dfs(1,30,0)%mod;
			}
		}
	}
	printf("%lld\n",ans);
//	fclose(stdin);fclose(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值