2022.9.22测试

一测:396pts,不知道为什么哈希假了。

T1:P7800 [COCI2015-2016#6] PAROVI(绿)

T2:P7859 [COCI2015-2016#2] GEPPETTO(绿)

T3:P7861 [COCI2015-2016#2] SAVEZ(蓝)(96pts)

T4:P7862 [COCI2015-2016#2] DRŽAVA(紫)

T1:

读完题后可以首先发现,编号 1n 的点都是必须要选的,否则 Slavko 可以选择 2 或者 n 取得胜利。

而对于中间的数如果存在两对数 (l1,r1) (l2,r2)l1<l2r1l2 那么可以选择数 l2 取胜。

那么只有 r1>l2 时,取 l2 是无法取胜的,此时若把两对数看成一个区间,那么就可以将本题看成一个左闭右开的区间覆盖问题。

由于求方案数,考虑dp。

预处理出来所有互质的数对,并按左区间为关键字排序。

fi,j 代表对于第 i 个区间,已经覆盖到第 j 格的方案数。

初始化:对于数对 (li,ri),如果 li=1,那么 fi,ri=1

转移:

选择这个区间:fi,max(j,ri)+=fi1,j(lij)

不选这个区间:fi,j+=fi1,j

答案即为 fnum,n其中 num 代表互质数对个数

复杂度约为 O(n3)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int mod=1e9;
const int N=25;
int n,cnt,f[N*N][N];
int gcd(int x,int y)
{
	if(y==0)return x;
	return gcd(y,x%y);
}
struct node
{
	int l,r;
}a[N*N];
int cmp(node fi,node se)
{
	return fi.r>se.r;
}
signed main()
{
	//freopen("parovi.in","r",stdin);
	//freopen("parovi.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)if(gcd(i,j)==1)a[++cnt]=(node){i,j};
	}
	for(int i=1;i<=cnt;i++)
	{
		if(a[i].l==1)f[i][a[i].r]=1;
		for(int j=1;j<=n;j++)
		{
			f[i][j]+=f[i-1][j];
			f[i][j]%=mod;
			if(a[i].l<=j)f[i][max(j,a[i].r)]+=f[i-1][j],f[i][max(j,a[i].r)]%=mod;
		}
	}
	int ans=f[cnt][n];
	printf("%lld",ans);
	return 0;
}

T2:

暴搜,完了。

对于每一个材料,只有选或不选,搜索时记录一下选择情况,再进行判断是否有冲突即可。

加个小优化,对于每一对冲突,按材料编号更大的排序,然后每次判断是否合法时,如果冲突的材料还未搜索到,就可以不用继续判断了。

复杂度 O(2n×m)

为什么习惯签到题放第二题啊。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int M=405;
const int N=25;
struct node
{
	int x,y;
}a[M];
int cmp(node fi,node se)
{
	return fi.y<se.y;
}
int n,m,vis[N];
int dfs(int x)
{
	for(int i=1;i<=m;i++)
	{
		if(a[i].y>=x)break;
		if(vis[a[i].x]&&vis[a[i].y])return 0;
	}
	if(x==n+1)return 1;
	int sum=0;
	vis[x]=0;
	sum+=dfs(x+1);
	vis[x]=1;
	sum+=dfs(x+1);
	return sum;
}
int main()
{
	//freopen("geppetto.in","r",stdin);
	//freopen("geppetto.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a[i].x,&a[i].y);
		if(a[i].x>a[i].y)swap(a[i].x,a[i].y);
	}
	sort(a+1,a+1+m,cmp);
	int ans=dfs(1);
	printf("%d",ans);
	return 0;
}

T3:

做这题的心路历程十分神奇。

看到题,想到哈希匹配。

对于每一个 xi 若可以为 xj 的前缀和后缀,处理 xi 的哈希值,求 xj 的前缀和后缀哈希即可。

然后本机上跑不过,就进行了分裂循环,优化调用 map 次数等等一系列优化。

结果发现数据比我想象中的水的多。

还是放一下第一次做的代码:代码

dpi 代表以第 i 个字符结尾的字序列长度最大值。

然后利用 map 存储之前每个字符串的哈希值,最后如果本次前缀和后缀都能匹配上之前的哈希值,那么可以进行转移。

dpi=max(dpj+1)(初始dpi=1)

最后求 max(dpi) 即可。

复杂度 O(i=1nxi×log(n))

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define int long long
using namespace std;
const int N=2e6+5;
const int mod1=1e15+37;
const int mod2=212370440130137957ll;
map<int,int>vis1,vis2;
int n,dp[N];
char s[N];
inline void read()
{
	int cnt=0;
	char ch=getchar();
	while(ch==' '||ch=='\n')ch=getchar();
	while(ch!=' '&&ch!='\n')
	{
		s[++cnt]=ch;
		ch=getchar();
	}
	s[++cnt]='\0';
}
signed main()
{
	//freopen("savez.in","r",stdin);
	//freopen("savez.out","w",stdout);
	scanf("%lld",&n);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		int len=strlen(s+1);
		int num1=0,num2=0,lim1=1,lim2=1;
		int num3=0,num4=0;
		dp[i]=1;
		for(int j=1;j<=len;j++)
		{
			num1=num1+(s[j]-'A'+2)*lim1;
			num1%=mod1;
			num2=num2+(s[j]-'A'+2)*lim2;
			num2%=mod2;
			num3=num3*30+(s[len-j+1]-'A'+2);
			num3%=mod1;
			num4=num4*30+(s[len-j+1]-'A'+2);
			num4%=mod2;
			lim1*=30;
			lim1%=mod1;
			lim2*=30;
			lim2%=mod2;
			int _1=vis1[num1],_2=vis2[num2],_3=vis1[num3],_4=vis2[num4];
			if(_1&&_3&&_1==_2&&_2==_3&&_3==_4)dp[i]=max(dp[i],dp[_1]+1);
		}
		vis1[num1]=i,vis2[num2]=i;
		ans=max(ans,dp[i]);
	}
	printf("%lld",ans);
	return 0;
}

T4:

适当进行骗分是真的有用。

40pts

对于每两个点建立一条边,然后在贪心每次求最小边,在期间进行01背包即可,01背包用于处理模数。

dpi,j 代表以 i 为编号的一个并查集,子集的和模数是否可以为 j

每次将 t 集合合并给 i 集合。

dpi,j|=dpt,j

再从 1n 枚举 k 集合中的数 p。(如果枚举 0 说明在此已经有答案了,所以枚举 0 无意义)

dpi,j|=dpt,(j+p)modk

当合并后 dpi,0=1,取这条边为答案即可。

非正解做法(这里注重考场上没想出来如何骗分,看正解可以移步其他题解,注:目前这个解法可以过):

一来想不太出来,但是推出个结论,要推出模数 0,最多需 k 个数。

证明:如果一个新的数加进来,不会产生其他模数,那么原来的模数是个等差数列,并且等差数列会从 0 开始,公差为新数,所以原来取的数都是一样的。如果不会产生模数,那么证明这些相同的数 x 满足 kmodx=0,那么此时只需要用 k÷x 即可推出模数 0,如果可以更新模数每一次更新一个,最多需要 k 步,若 x=1 也是 k 步,即可证。

那么可以想到其实只需要求一个点离的最近的 k 个点即可。

如果在考场想不到二分什么的,应该考虑揣摩出题人心理,进行关键字排序。

对于每个点 (x,y)

x 为关键字排序:160pts

y 为关键字排序:160pts

x2+y2 为关键字排序:160pts

x×y 为关键字排序:160pts

x2+y22x2y 为关键字排序:160pts

……

好吧当我没说。

这里选择第三种排序,排序后取在自己后面的点,这里因为 k30,所以选择 40 个点稳一点。

最后与上面做法就一样了,多加了个启发式合并,对于单独一个点的并查集插入进行特殊处理,不用也可以过。

总结:如果考试想不出来,利用一些非正常办法也可以拿高分的。

复杂度 O(k×n×log(n))

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
const int N=5e4+5;
int n,k,cnt,f[N],siz[N],s[N][35],q[N];
struct node
{
	int x,y,data;
}a[N];
int cmp(node fi,node se)
{
	return fi.x*fi.x+fi.y*fi.y<se.x*se.x+se.y*se.y;
}
struct node2
{
	int from,to,data;
}t[N*40];
int cmp2(node2 fi,node2 se)
{
	return fi.data<se.data;
}
int afind(int x)
{
	if(f[x]==x)return x;
	return f[x]=afind(f[x]);
}
int krus()
{
	for(int i=1;i<=cnt;i++)
	{
		int from=afind(t[i].from),to=afind(t[i].to);
		if(from==to)continue;
		if(siz[to]==1)swap(from,to);
		for(int j=1;j<k;j++)q[j]=0;
		for(int j=1;j<k;j++)
		{
			if(!s[to][j])continue;
			if(siz[from]==1)q[(j+a[from].data)%k]=1,q[a[from].data]=1;
			else
			{
				for(int p=1;p<k;p++)
				{
					if(!s[from][p])continue;
					q[(j+p)%k]=1;
					q[p]=1;
				}
			}
		}
		for(int j=0;j<k;j++)s[to][j]|=q[j];
		siz[to]+=siz[from];
		f[from]=to;
		if(s[to][0])return t[i].data;
	}
	return t[cnt].data;
}
signed main()
{
	//freopen("drzava.in","r",stdin);
	//freopen("drzava.out","w",stdout);
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].data),a[i].data%=k;
	sort(a+1,a+1+n,cmp);
	int kt=2000000ll/n;
	for(int i=1;i<=n;i++)
	{
		f[i]=i;
		siz[i]=1;
		s[i][a[i].data]=1;
		for(int j=i+1;j<=min(n,i+kt);j++)t[++cnt]=(node2){i,j,(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)};
	}
	
	sort(t+1,t+1+cnt,cmp2);
	double ans=sqrt(1.0*krus());
	printf("%.3lf",ans);
	return 0;
}
/*
6 11
0 0 1
0 1 2
1 0 3
1 1 4
5 5 1
20 20 10
*/
posted @   Gmt丶Fu9ture  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示