ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022,7 暑假集训

2022-07-24 21:06:29  阅读:127  来源: 互联网

标签:2022 int 差分 暑假 序列 操作 include 集训 sim


7.11 C 乱搞题

有 \(n\) 个桶,每个桶中装有 \(n\) 个数。保证 \(1 \sim n\) 中的每种数字在所有桶中一共出现恰好 \(n\) 次。

每次操作选择一个区间 \([l,r]\),若满足第 \(l \sim r\) 个桶最上端的数相同,可以将这些桶最上端的数一起取出。

求至少需要多少次操作才能取出所有的数。

保证数据随机

数据范围

\(1 \leq n \leq 1000\)

思路

考虑将相邻两个桶内的相同的数连一条边。可以发现如果两条边不存在交叉,就存在一种方案使得这两组数都可以同时被拿出,也就可以减少两次操作数。而如果想要选择最多不相交的边,不难发现就是求两个序列的最长公共子序列。

一般求 LCS 的方法是dp,时间复杂度为 \(O(n^2)\),但由于本题数据保证随机,可以在dp的基础上优化。

对于第二个序列,记录每一个值出现的位置,并且从后往前遍历第一个序列,查询在第二个序列中以相同字符下标前的数字结尾的公共字串的最大值,可以用树状数组维护。由于数据随机,相邻两列之间相同对数的期望为 \(O(n)\)。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1010;
int a[N][N],n,c[N],ans,f[N];
vector<int>id[N];
int lowbit(int x){return x&-x;}
void update(int x,int k){while(x<=n){c[x]=max(c[x],k);x+=lowbit(x);}}
int query(int x){int res=0;while(x){res=max(res,c[x]);x-=lowbit(x);}return res;}
int solve(int a[],int b[])// LCS
{
	for(int i=1;i<=n;i++) id[b[i]].push_back(i);
	for(int i=1;i<=n;i++)
	{
		for(auto j:id[a[i]]) f[j]=query(j-1)+1;
		for(auto j:id[a[i]]) update(j,f[j]);
	}
	int res=query(n);memset(c,0,sizeof(c));
	for(int i=1;i<=n;i++) id[i].clear();return res;
}
int main()
{
//	freopen("rand.in","r",stdin);
//	freopen("rand.out","w",stdout);
	scanf("%d",&n);ans=n*n;for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
	for(int i=1;i<n;i++) ans-=solve(a[i],a[i+1]);printf("%d\n",ans);
	return 0;
}

7.11 D 奇怪题

有 \(n\) 个七位数,记作 \(a_1 \sim a_n\)。

对七位数 \(v\) 的一次位移会将 \(v\) 的最高位移到最低位,其余位相对顺序保持不变。例如 \(1234567\) 在一次位移后会变
成 \(2345671\)。

每次操作可以选定一段区间 \([l,r]\) 和一个整数 \(k\),对 \(a_l \sim a_r\) 分别进行 \(k\) 次位移。
求至少需要多少次操作,才能将所有 \(a_i\) 都调整成 可能的最大值。

数据范围

\(1 \leq n \leq 501\)

思路

首先可以将七位数字都相同的数删去;对于剩下的数,由于 \(7\) 是一个质数,可能的最大值只能在一个位置取到,设为 \(c_i\)。

那么题目就转化为每次选择一个区间,给区间内的每一个数都加上 \(k\),求最小的操作次数使得所有的数在模 \(7\) 意义下都为 \(0\)。

继续考虑差分,先对原序列求一个差分数组,每次的区间加操作就转化为给差分序列中的一个数加上一个数,另一个数减去相同的数。现在就是要用最少的操作次数使得差分序列中的每一个数在模 \(7\) 意义下都为 \(0\)(下面简称为 \(0\))。

对于原差分序列的一个有 \(x\) 个元素的子集,如果这 \(x\) 个元素的和为 \(0\),那么每次对集合中的两个数进行操作不会改变和的大小,那么必然能在 \(x-1\) 次操作内将这些元素都变成 \(0\)。那么现在就是要在满足和为 \(0\) 的情况下,将原差分序列中的 \(n+1\) 的数分成尽可能划分成多的子集。

首先可以将序列中和为 \(0\) 的两个数单独拎出来构成一个集合,这样操作后就剩下之多 \(3\) 种不相同的数。

设 \(f_{i,j,k}\) 表示三种数分别剩余 \(i,j,k\) 个时所需的最少次数。由于转移的方案特别多,可以首先剪去很多劣的方案。对于不劣的方案,显然满足以下两点:

  1. \(a,b,c<7\)。如果一个数大于 \(7\) 了,那么显然把这 \(7\) 个数单独放一个集合更优。

2.\(gcd(a,b,c)=1\),如果他们的最大公约数不为 \(1\),那么就可以把这个集合拆分成若干个相等的子集,显然不是优的方案。

这样一稿剩余的合法转移方案数就很少了,可以通过本题。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=521,T=1e7;
const int YY[9]={1111111,2222222,3333333,4444444,5555555,6666666,7777777,8888888,9999999};
int m,id[N],c[N],ans,n,a[N],cnt[N];
short f[N][N][N];
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
struct node{int a,b,c;}p[N];
int main()
{
//	freopen("strange.in","r",stdin);
//	freopen("strange.out","w",stdout);
	scanf("%d",&n);
	for(int x,i=1;i<=n;i++)
	{
		scanf("%d",&x);int j,maxv=0;
		for(j=0;j<9;j++) if(x==YY[j]) break;
		if(j!=9) {i--,n--;continue;}
		for(j=0;j<7;j++)
		{
			if(x>maxv) maxv=x,a[i]=j;
			x=x*10%T+x*10/T;
		}
	}
	for(int i=n+1;i>=1;i--) a[i]=(a[i]-a[i-1]+7)%7,cnt[a[i]]++;
	for(int x,i=1;i<=3;i++) x=min(cnt[i],cnt[7-i]),ans+=x,cnt[i]-=x,cnt[7-i]-=x;
	for(int i=1;i<=6;i++) if(cnt[i]) c[++m]=cnt[i],id[m]=i;m=0;
	for(int i=0;i<7;i++)
	    for(int j=0;j<7;j++)
	        for(int k=0;k<7;k++)
//	            if((i||j||k)&&(i*id[1]+j*id[2]+k*id[3])%7==0) p[++m]=node{i,j,k};
	            if((i||j||k)&&(i*id[1]+j*id[2]+k*id[3])%7==0&&gcd(gcd(i,j),k)==1) p[++m]=node{i,j,k};
	memset(f,0x3f,sizeof(f));f[0][0][0]=0;
	for(int i=0;i<=c[1];i++)
	    for(int j=0;j<=c[2];j++)
	        for(int k=0;k<=c[3];k++)
	            for(int t=1;t<=m;t++)
	                if(i>=p[t].a&&j>=p[t].b&&k>=p[t].c)
	                    f[i][j][k]=min(f[i][j][k],f[i-p[t].a][j-p[t].b][k-p[t].c]+p[t].a+p[t].b+p[t].c-1);
	printf("%d\n",f[c[1]][c[2]][c[3]]+ans);
	return 0;
}

标签:2022,int,差分,暑假,序列,操作,include,集训,sim
来源: https://www.cnblogs.com/NLCAKIOI/p/16515397.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有