码迷,mamicode.com
首页 > 其他好文 > 详细

CF1286F Harry The Potter

时间:2020-05-27 20:40:22      阅读:73      评论:0      收藏:0      [点我收藏+]

标签:har   sqrt   clu   lang   element   使用   区间   getch   应该   

题目传送门

分析:
首先知道答案不会超过\(n\),做\(n\)次操作1绝对完成任务了
我们考虑用操作2替换操作1减少次数
我们将整个序列看做\(n\)个点,操作2将其中两个点相连
首先我们不会连出环,这样环上的点全都可以使用操作1,无法达到减少操作次数的目标
没环?那就是森林了呗
考虑其中的一个子集构成了树,相连的两个点虽然相减的值不一样,但是出现的差异最多不超过子集大小(每一条边只贡献1的差异)
如果把这1的微小差距忽略,那么相连的两点在“大体上”同增同减,相距为2的点在“大体上”你减我加,总和“大体上”不变
很容易(并不)联想到二分图,同部的点总和“大体上”不变
于是我们枚举某个集合\(S\)的子集,将集合一分为二为\(A,B\)
如果\(|Sum_A-Sum_B|<|S|\)便说明这个划分所带来的差异是在接受范围的
由于集合\(S\)会形成\(|S|-1\)条边,所以\(|Sum_A-Sum_B|\)还要和\(|S|-1\)同奇偶
满足如上两个条件的集合,完成该集合的任务可以少操作一次
接下来考虑如何合并子集
我很菜,表示到这一步已经很烧脑了,写\(O(3^n)\)剪剪枝九秒应该能过
(代码是\(O(3^n)\)的)
究极巨佬说可以达到\(O((1+\sqrt 2)^n+2^{n}n^{2}logn)\)
我tm直呼内行.jpg

前一个部分是在枚举子集求合法可连成树的集合时,将每个集合分成两部分排序后寻找合法区间中的值
复杂度分析:

\(~~~~O(\sum_{k=0}^{n}C_n^{k}2^{\frac{k}{2}})\)
\(=O(\sum_{k=0}^{n}C_n^{k}\sqrt 2^k)\)
\(=O((1+\sqrt 2)^n)\)

(二项式定理)
真是流氓的复杂度。。。

后一个部分,做一个生成函数\(F(x)\)
合法的集合的位置的值为1,其余为0
我们来子集卷积
考虑做\(p\)次卷积,整个生成函数不为0,说明其中能够找到一个集合,能够被\(p\)个不相交集合合并起来形成,那么就可以减少p次操作
找到第一个\(p\)使得\(F(x)^p=0\)就好了
具体过程和树上倍增相似,写起来太精污了就不写了(

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<algorithm>
 
#define maxn 2000005
#define INF 0x3f3f3f3f
#define MOD 998244353
 
using namespace std;
 
inline long long getint()
{
	long long num=0,flag=1;char c;
	while((c=getchar())<‘0‘||c>‘9‘)if(c==‘-‘)flag=-1;
	while(c>=‘0‘&&c<=‘9‘)num=num*10+c-48,c=getchar();
	return num*flag;
}
 
int n;
long long a[maxn],sum[maxn];
int ans[maxn],sz[maxn];
int vis[maxn];
 
int main()
{
	n=getint();
	for(int i=0;i<n;i++)
	{
		a[i]=getint();
		if(!a[i])n--,i--;
	}
	int S=(1<<n)-1;
	for(int i=0;i<=S;i++)for(int j=0;j<n;j++)if(i&(1<<j))sum[i]+=a[j];
	for(int i=0;i<=S;i++)
	{
		sz[i]=sz[i>>1]+(i&1);
		for(int j=(i-1)&i;((j<<1)>=i)&&j;j=(j-1)&i)
		{
			int tmp=i^j;
			long long num=abs(sum[j]-sum[tmp]);
			if(num<sz[i]&&((sz[i]-num)&1)){vis[i]=1;break;}
		}
	}
	for(int i=1;i<=S;i++)if(vis[i])
	{
		ans[i]=max(ans[i],1);int t=S^i;
		for(int j=t;j;j=(j-1)&t)ans[i|j]=max(ans[i|j],ans[j]+1);
	}
	printf("%d\n",n-*max_element(ans+1,ans+S+1));
}

技术图片

CF1286F Harry The Potter

标签:har   sqrt   clu   lang   element   使用   区间   getch   应该   

原文地址:https://www.cnblogs.com/Darknesses/p/12976093.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!