CSP模拟赛1-斐波那契数颜色分组

rank 22 分数 70+40+32

T1:找规律;T2:分块(数据结构);T3:暴力+优化?

T1:只说一句:1e12是12个0,不是12位

T2:给你一个数列,支持:(1)查询区间的某个数字出现次数(2)把区间内的相邻数字交换位置

二分位置
用vector g[x]存储每种数字出现的位置,查询直接lower_bound,在修改的时候,可以发现如果cor[x]==cor[x+1]没有对g[x]的实际影响;不一样的时候,g[x]的相对位置也不会改变,所以只需要2次lower_bound找出位置把对应位置修改就行\(O(m*log(n))\)

点击查看代码






#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<iomanip>
#include<bitset>
#include<map>
#include<queue>
#include<bitset>
#include<deque>
#include<vector>
#define _f(i,a,b)  for(register int i=a;i<=b;++i)
#define f_(i,a,b)  for(register int i=a;i>=b;--i)
#define ll long long
#define rint register int
#define chu printf
using namespace std;
inline int re()
{
	int x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')h=-1;ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);ch=getchar();
	}
	return x*h;
}
const int N=3e5+100;
int n,m,a[N];
vector<int>c[N];
int main()
{
	//freopen("c.in","r",stdin);
	n=re(),m=re();
	_f(i,1,n)
	{
		a[i]=re();
		c[a[i]].push_back(i);
	}
	int opt,l,r,x;
	while(m--)
	{
		opt=re();
		if(opt==1)
		{
			l=re(),r=re(),x=re();
int R=upper_bound(c[x].begin(),c[x].end(),r)-c[x].begin()-1;
int L=lower_bound(c[x].begin(),c[x].end(),l)-c[x].begin();
chu("%d\n",R-L+1);
		}
		else
		{
			x=re();
			if(a[x]==a[x+1])continue;
			else
			{
int L=lower_bound(c[a[x]].begin(),c[a[x]].end(),x)-c[a[x]].begin();
c[a[x]][L]=x+1;
int R=lower_bound(c[a[x+1]].begin(),c[a[x+1]].end(),x+1)-c[a[x+1]].begin();
c[a[x+1]][R]=x;
swap(a[x],a[x+1]);
			}
		}
	}
	return 0;
}
/*
*/

T3:给你一个序列,要求把序列分成n组,当K=1,每组只有一个团体,团体内部的数任意2个加和!=平方数。当K=2,每组有2个团体,团体内部要求一样。团体编号可以不连续,组别编号必须连续。求最少组别和断点位置。(n<=140000)

首先一个贪心的思路就是从后往前划分,尽量每组分最多元素,一定最优。因为假如我可以分到pos,但是我只选到pos+a,那么对于前面的,只会让限制变多,更难分到一个组,所以应该尽量往前。
对于K=1,主要优化判断平方数判断方式,在值域上维护(最多512种平方数组合)是否访问。清空时只需要清空i~last的贡献区间(不然会T)
对于K=2,我们发现【l,r】的区间可以在一组,当且仅当有矛盾的任意数连边后满足二分图。
方法一:二分图染色判断对于pos位置,是否可以加到pos+1~last位置里。
用vector建图,在染色时,只需要从pos开始就行(对于后面的一定构成二分图,不然就不合法了),消除影响也只需要把pos位置的点都消边就行,因此复杂度就是
O(n*根号n)的
,虽然看起来染色会遍历很多点,但是染色的时候对于每个数大致512条左右的边(矛盾关系),重复的?概率很小嘛。所以也是对的。

点击查看代码






#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<iomanip>
#include<bitset>
#include<map>
#include<queue>
#include<bitset>
#include<deque>
#include<vector>
#define _f(i,a,b)  for(register int i=a;i<=b;++i)
#define f_(i,a,b)  for(register int i=a;i>=b;--i)
#define ll long long
#define rint register int
#define chu printf
using namespace std;
inline int re()
{
	int x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')h=-1;ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);ch=getchar();
	}
	return x*h;
}
const int N=131072+100;
int cor[N],n,K;
int al[N],cnt;
int vis[131072+100],se[131072+100],code,mx,cmx;
int ans[N],tot;
vector<int>g[N];//图
void deal1()
{
	//chu("code:%d\n",code);
	int lst=n;//上一个分割线左侧
	vis[cor[n]]=1;
	f_(i,n-1,1)//现在在加入哪个位置
	{
		bool is=0;
		// _f(j,i+1,lst)//检查是不是平方数
		// {
		// 	int num=cor[j]+cor[i],pf=sqrt(num);
		// 	if(num==pf*pf)
		// 	{
		// 		is=1;break;
		// 	}
		// }
		f_(j,code,0)
		{
			if(j*j-cor[i]<0)break;
			if(j*j-cor[i]>mx)continue;//这里会炸
			if(vis[j*j-cor[i]])
			{
				is=1;break;
			}
		}
		if(is==0){}//如果不是平方数,可以加到lst里面
		else
		{
			al[++cnt]=i;
            _f(j,i,lst)vis[cor[j]]=0;//从新开始,但是这个数要算在里面
			lst=i;
		
		}
		vis[cor[i]]=1;
	}
	chu("%d",cnt+1);//
	chu("\n");
	f_(i,cnt,1)chu("%d ",al[i]);
}
inline bool check(int rt,int fa,int yan)
{
	se[rt]=yan;
	for(rint to:g[rt])
	{
		if(to==fa)continue;
		if(se[to])
		{
			if(se[to]==se[rt])return 0;
		}
		else if(!check(to,rt,yan^1))return 0;
	}
	return 1;//没边就没矛盾~
}
int main()
{
	n=re(),K=re();
	_f(i,1,n)
	{
		cor[i]=re();
		if(cor[i]>mx)cmx=mx,mx=cor[i];
		else if(cor[i]>cmx)cmx=cor[i];
	}
	code=sqrt(mx+cmx);//最多的平方数种类
	if(K==1)
	{
		deal1();return 0;
	}
	_f(i,0,code)vis[i*i]=1;//平方数直接vis1
	int lst=n;
	f_(i,n-1,1)
	{
		_f(j,i+1,lst)
		if(vis[cor[j]+cor[i]])
		g[i].push_back(j),g[j].push_back(i);//erase??
		if(!check(i,0,2))
		{
			ans[++tot]=i;lst=i;se[i]=0;
			g[i].clear();
		}
		else
		{
			_f(j,i,lst)se[j]=0;
		}
	}
	chu("%d\n",tot+1);
	f_(i,tot,1)chu("%d ",ans[i]);
	return 0;
}
/*
2 2
1 3

5 1
1 3 15 10 6
对于K=1的优化了一下枚举方式

5 2
1 3 15 10 6
*/

二:拓展域并查集
维护矛盾关系。细节很多。
比如对于一个cor*2是平方数的特殊处理。

点击查看代码

<details>
<summary>点击查看代码</summary>

include

include

include

include

include

include

include

include

include

include

include

include

include

include

define _f(i,a,b) for(register int i=a;i<=b;++i)

define f_(i,a,b) for(register int i=a;i>=b;--i)

define ll long long

define rint register int

define chu printf

using namespace std;
inline int re()
{
int x=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch'-')h=-1;ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();
}
return xh;
}
const int N=131072+100;
int cor[N],n,K;
int al[N],cnt;
int vis[131072+100],fa[N
2],pf[N*2],code,mx,cmx;
int ans[N],tot;
void deal1()
{
//chu("code:%d\n",code);
int lst=n;//上一个分割线左侧
vis[cor[n]]=1;
f_(i,n-1,1)//现在在加入哪个位置
{
bool is=0;
// _f(j,i+1,lst)//检查是不是平方数
// {
// int num=cor[j]+cor[i],pf=sqrt(num);
// if(num
pfpf)
// {
// is=1;break;
// }
// }
f_(j,code,0)
{
if(j
j-cor[i]<0)break;
if(jj-cor[i]>mx)continue;//这里会炸
if(vis[j
j-cor[i]])
{
is=1;break;
}
}
if(is==0){}//如果不是平方数,可以加到lst里面
else
{
al[++cnt]=i;
_f(j,i,lst)vis[cor[j]]=0;//从新开始,但是这个数要算在里面
lst=i;

	}
	vis[cor[i]]=1;
}
chu("%d",cnt+1);//
chu("\n");
f_(i,cnt,1)chu("%d ",al[i]);

}
inline int getfa(int x)
{
return (xfa[x])?x:(fa[x]=getfa(fa[x]));
}
int main()
{
n=re(),K=re();
_f(i,1,n)
{
cor[i]=re();
if(cor[i]>mx)cmx=mx,mx=cor[i];
else if(cor[i]>cmx)cmx=cor[i];
}
code=sqrt(mx+cmx);//最多的平方数种类
if(K
1)
{
deal1();return 0;
}
f(i,0,code)pf[ii]=1;
f(i,1,n)fa[cor[i]+mx]=cor[i]+mx,fa[cor[i]]=cor[i];
int lst=n;vis[cor[n]]=1;
f
(i,n-1,1)
{
// chu("check:%d\n",i);
bool fl=1;
if(vis[cor[i]]&&pf[cor[i]
2])//如果说已经出现过
{
if((fa[cor[i]+mx]!=cor[i]+mx||vis[cor[i]]==2))//是平方数而且已经有限制了或者出现3个没法弄了
fl=0;
}
else
{
// chu("df\n");
f
(j,code,0)
{
if(jj-cor[i]>mx)continue;
if(j
j-cor[i]<=0)break;
if(!vis[jj-cor[i]])continue;//cor[i] jj-cor[i]
// chu("par:%d and %d\n",jj-cor[i],cor[i]);
if(pf[2
(jj-cor[i])]&&vis[jj-cor[i]]2)
{
fl=0;break;
}
int fa_bas=getfa(cor[i]),fa_emy=getfa(j*j-cor[i]);
if(fa_bas
fa_emy)
{
fl=0;break;
}
int fa_bas_tu=getfa(cor[i]+mx),fa_emy_tu=getfa(jj-cor[i]+mx);
fa[fa_emy_tu]=fa_bas;
fa[fa_bas_tu]=fa_emy;
// chu("add:%d--%d\n",cor[i],j
j-cor[i]);
}
}
if(!fl)//如果必须断
{
ans[++tot]=i;
f(j,i,lst)vis[cor[j]]=0,fa[cor[j]]=cor[j],fa[cor[j]+mx]=cor[j]+mx;
lst=i;
}
vis[cor[i]]++;
}
chu("%d\n",tot+1);
f
(i,tot,1)chu("%d ",ans[i]);
return 0;
}
/*
5 2
1 3 15 10 6
*/

</details>

posted on 2022-09-03 19:24  HZOI-曹蓉  阅读(48)  评论(0编辑  收藏  举报