zoukankan      html  css  js  c++  java
  • [洛谷P3940]分组

    题目

    思路

    好题+细节题

    答案字典序要求最小,所以考虑倒叙枚举,对于当前一组需要尽量多的加东西,因为后面组选的数越多,前面的选择机会越多

    枚举序列枚举值域,这是这道题的关键

    (K=1):倒叙枚举到(i),此时只需判断当前组中的数是否有加(a_i)等于完全平方数的;可以(O(n))枚举,但显然可以更优:枚举所有的完全平方数,对于之前的数开桶记录即可,这样做的复杂度为完全平方数的个数,可以发现最大为512,可以通过这部分数据

    (K=2):同理倒叙枚举到(i),此时分两种情况:

    1. 一般情况下,(a_i)第一次出现在这一组中;“如果有与(a_i)成为完全平方数的数,那么它们俩不能在同一边”->“两个点不能在同一边”,这是不是很眼熟?就是扩展域并查集好题(裸题关押罪犯,所以直接套用这个做法即可;优化:直接枚举序列仍然是(O(n^2))的,所以令(f_i)表示与(i)为友的集合,(f_{i+maxx})表示与(i)为敌的集合,直接合并并查集就好了

    2. (a_i)多次出现且(2 imes a_i)为完全平方数,上面的值域并查集并不能很好的处理这种自己和自己的关系,所以需要特判

    细节:上面的1无法考虑到另一个数自己和自己成完全平方的情况,需要特殊处理,在代码中有注释(如果没有考虑到会错后面几个点)

    全部写清楚就是这样的。。。

    Code

    #include<bits/stdc++.h>
    #define N 150005
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define Max(x,y) ((x)>(y)?(x):(y))
    #define re register
    using namespace std;
    typedef long long ll;
    int temp=512,c=512*512+1;
    int n,k,a[N],ct[N],tot;
    bool exist[N<<2];
    
    template <class T>
    void read(T &x)
    {
    	char c; int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
    }
    bool check1(int x)
    {
    	for(re int i=temp;i>=1;--i)
    	{
    		int c=i*i-x;
    		if(c<=0) break;//颜色默认为正数? 
    		if(exist[c]) return 0;
    	}
    	return 1;
    }
    void solve1()
    {
    	int l=n;
    	for(int i=n;i>=1;--i)
    	{
    		if(check1(a[i])) exist[a[i]]=1;
    		else//i为新的开始 
    		{
    			for(int j=l;j>i;--j) exist[a[j]]=0;
    			//清零当然不能么么set啦qwq 
    			exist[a[i]]=1;
    			ct[++tot]=i;
    			l=i;
    		}
    	}
    	printf("%d
    ",tot+1);
    	for(int i=tot;i>=1;--i) printf("%d ",ct[i]);
    	printf("
    ");
    }
    
    int fa[N<<4],vis[N<<4];
    bool tag[N<<4];
    int find(int x) {return x==fa[x] ? x : fa[x]=find(fa[x]);}
    int merge(int x,int y)
    {
    	int fx=find(x),fy=find(y);
    	if(fx==fy) return 0;
    	fa[fx]=fy;
    	return 1;
    }
    void solve2()
    {
    	int l=n;
    	for(int i=1;i*i<=c*2;i++) tag[i*i]=1;
    	for(int i=1,t=(c<<1);i<=t;++i) fa[i]=i;
    	for(int i=n;i>=1;--i)
    	{
    		bool no=0;
    		if(vis[a[i]])//重复出现 
    		{
    			if(tag[2*a[i]])//分在不同集合,否则一个集合不管 
    			{
    				if(vis[a[i]]==2) no=1;
    				//自己已有两个 
    				else for(int j=temp;j>=1;--j)
    				{
    					if(a[i]>j*j) break;
    					if((vis[j*j-a[i]]&&j*j!=a[i]*2)) { no=1; break; } 
    				}
    			}
    		}
    		else for(int j=temp;j>=1;--j)
    		{
    			if(a[i]>j*j) break;
    			if(vis[j*j-a[i]])
    			{
    				if(tag[2*(j*j-a[i])]&&vis[j*j-a[i]]==2) no=1;
    				//如果它出现了两次 且 在两边,直接判负
    				//下面的操作无法判断上面这种情况 
    				if(find(a[i])==find(j*j-a[i])) no=1;
    				merge(a[i]+c,j*j-a[i]);
    				merge(j*j-a[i]+c,a[i]);
    			}
    			if(no) break;
    		}
    		if(no)
    		{
    			for(int j=i;j<=l;++j) vis[a[j]]=0,fa[a[j]]=a[j],fa[a[j]+c]=a[j]+c;
    			ct[++tot]=i;
    			l=i;
    		}
    		vis[a[i]]++;
    	}
    	printf("%d
    ",tot+1);
    	for(int i=tot;i>=1;--i) printf("%d ",ct[i]);
    	printf("
    ");
    }
    int main()
    {
    	freopen("division.in","r",stdin);
    	freopen("division.out","w",stdout);
    	read(n);read(k);
    	for(int i=1;i<=n;++i) read(a[i]);
    	if(k==1) solve1();
    	else solve2();
    	return 0;
    }
    
  • 相关阅读:
    Java--强制转化
    Java--成绩分类
    建造者模式的应用
    抽象工厂模式的应用
    工厂模式
    第几天
    Stacking Plates(存档待续.....(没有写思路和程序))
    圆的面积
    公孙策和陆湘湘对话(少年包青天第二部第二十集末尾和第二十一集开头部分)
    简单接口回调例子
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11716416.html
Copyright © 2011-2022 走看看