首页 > 其他分享 >P3213 [HNOI2011]勾股定理 题解

P3213 [HNOI2011]勾股定理 题解

时间:2023-02-24 13:46:29浏览次数:50  
标签:int 题解 枚举 2xy HNOI2011 P3213 include 仙人掌 dp

据说是NP问题。

很明显我们要先预处理出来勾股数对。

但由于数过于大,所以常规的枚举是解决不了问题的。

但也貌似没有什么很好的办法可以立马找到一个数的勾股数对。

所以只能缩减枚举范围。

已知:

\[\begin{aligned} (x-y)^2+4xy&=(x+y)^2\\ (x^2-y^2)^2+4x^2y^2&=(x^2+y^2)^2\\ (x^2-y^2)^2+(2xy)^2&=(x^2+y^2)^2 \end{aligned}\]

所以可以把 \(A\) 看成 \((x-y)\),\(B\) 看成 \(2xy\),然后枚举 \(i,j\),代表式子中的 \(x,y\),即可缩小枚举范围。因为要满足 \(x^2-y^2\le10^6\hspace{0.3cm}2xy\le10^6\)。枚举复杂度约为 \(O(n)\)。

然后对于每一对勾股数对,连接一条边,即可得到一个森林。

这样就可以进行树形dp了。

设 \(dp_{i,(0,1)}\) 代表选与不选数 \(i\) 的方案数。

如果不选,方案即为自己儿子所有方案的乘积

\[dp_{i,0}=\prod_{(i,j)} dp_{j,0}+dp_{j,1} \]

若选,则自己儿子不能选,乘上自己儿子不选的方案数与选这个数的子集数(不算空集)即可。

\[dp_{i,1}=2^{num_i}\prod_{(i,j)}dp_{j,0} \]

然后对每棵树树形dp即可。

复杂度 \(O(n)\)。

但是交上去不对,那是因为在这之前的所有假设都是基于建出来的是棵树。

那接下来就麻烦了。

但是通过楼上大佬的模拟,发现在本题的数据范围内建出来的仅仅是棵仙人掌,这样NP问题就可以转换为普通问题了,那么我们就可以进行仙人掌dp。

首先利用Tarjan思想,对于每个整棵仙人掌遍历一遍,建立时间戳,寻找环。

找到环以后,我们考虑断掉环上的一条边,并记录下来边两边的点。

然后对于每一棵仙人掌,对记录下来的点的状态进行搜索,然后在搜索终端判断两边的点是否同为 \(1\)。如果满足都不同为 \(1\),进行一次树形dp。

那么这棵仙人掌所带来的方案数即为 \(dp_{i,0}+dp_{i,1}\)。

最后把所有仙人掌所得来的答案相乘,减去全部为空集情况,即减去一,就可得到答案。

#include<iostream>
#include<cstdio>
#include<vector>
#define int long long
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
int n,num[N],p[N],vis[N],ins[N],dfn[N],cnt,dp[N][2],chose[N],col[N],color;
vector<int>a[N],t;
int gcd(int x,int y)
{
	if(y==0)return x;
	return gcd(y,x%y);
}
void insert(int x)//记录断掉的边两边的点。
{
	if(!vis[x])
	{
		vis[x]=1;
		t.push_back(x);
	}
}
void build(int x,int fa)//找环。
{
	dfn[x]=++cnt;
	int len=a[x].size();
	for(int i=0;i<len;i++)
	{
		if(a[x][i]==fa)continue;
		if(!dfn[a[x][i]])build(a[x][i],x);
		else if(dfn[a[x][i]]<dfn[x])insert(a[x][i]),insert(x);
	}
}
bool check(int x,int fa)//判断断掉的边的两点是否符合条件
{
	col[x]=color;
	int len=a[x].size();
	for(int i=0;i<len;i++)
	{
		if(a[x][i]==fa)continue;
		if(chose[x]&&chose[a[x][i]])return false;
		if(col[a[x][i]]!=color&&!check(a[x][i],x))return false;
	}
	return true;
}
void get_ans(int x,int fa)//树形dp。
{
	col[x]=color;
	int len=a[x].size();
	dp[x][0]=1;
	dp[x][1]=p[num[x]]-1;
	if(vis[x])
	{
		if(chose[x])dp[x][0]=0;
		else dp[x][1]=0;
	}
	for(int i=0;i<len;i++)
	{
		if(a[x][i]==fa||col[a[x][i]]==color)continue;
		if(col[a[x][i]]!=color)get_ans(a[x][i],x);
		dp[x][1]*=(dp[a[x][i]][0]);
		dp[x][1]%=mod;
		dp[x][0]*=(dp[a[x][i]][0]+dp[a[x][i]][1]);
		dp[x][0]%=mod;
	}
}
int dfs(int x,int step)//搜索。
{
	if(step==t.size())
	{
		color++;
		if(check(x,0))
		{
			color++;
			get_ans(x,0);
			return dp[x][0]+dp[x][1];
		}
		return 0;
	}
	chose[t[step]]=0;
	int sum=0;
	sum+=dfs(x,step+1);
	chose[t[step]]=1;
	sum+=dfs(x,step+1);
	return sum%mod;
}
int solve(int x)
{
	t.clear();
	build(x,0);
	int sum=dfs(x,0);
	return sum;
}
signed main()
{
	scanf("%lld",&n);
	for(int i=1,x;i<=n;i++)scanf("%lld",&x),num[x]++;
	p[0]=1;
	for(int i=1;i<=(N-5);i++)p[i]=p[i-1]*2,p[i]%=mod;
   //以下为预处理。
	for(int i=1;i<=(N-5)/2;i++)
	{
		for(int j=i+1;2*i*j<=(N-5)&&j*j-i*i<=(N-5);j++)
		{
			if(((i&1)^(j&1))&&gcd(i,j)==1)
			{
				int u=j*j-i*i,v=2*i*j;
				if(!num[u]||!num[v])continue;
				a[u].push_back(v);
				a[v].push_back(u);
			}
		}
	}
	int ans=1;
	for(int i=1;i<=N-5;i++)if(num[i]&&!col[i])ans=ans*solve(i)%mod;
	printf("%lld",ans-1);
	return 0;
}

标签:int,题解,枚举,2xy,HNOI2011,P3213,include,仙人掌,dp
From: https://www.cnblogs.com/gmtfff/p/p3213.html

相关文章

  • P3989 [SHOI2013]阶乘字符串 题解
    由于一些不可抗拒的原因,\(n\ge22\)无解。那么只用考虑\(n\le21\)的情况即可。由于\(n\)的范围缩小,导致状压又可以重新使用,所以考虑状压。设\(f_i\)为\(i\)中......
  • AT655 玉座の間 题解
    首先我们要学习一下费用流。费用流是什么呢,可以理解为边带权值的网络流。那么最小费用最大流,是指在满足最大流的情况下的最小费用。那么我们就要实现这个过程。首先对......
  • P3997 [SHOI2013]扇形面积并 题解
    理解题意后可以把题目看成一个覆盖线段的问题。对于点在\(-m\)上,看成在\(m\)上。对于\(l<r\),不用处理。对于\(l>r\),将问题看成\((l,m)\)和\((-m+1.r)\)两个区......
  • P5616 [MtOI2019]恶魔之树 题解
    期望就是来搞笑的。由于有最小公倍数,所以可以想到分解质因数,对于多个数求最小公倍数,取每个质因子的最大指数,最后相乘即可。既然都知道了这个,那么就想到先统计每个数的个......
  • CF10E Greedy Change 题解
    一个非常离谱的题。首先有结论,如果有\(w\)使贪心不为最优解,那么比\(w\)小的第一个\(a_i\),用贪心法求面值为\(a_i-1\),除了最后选的一个数\(a_j\)会比原方法多选一......
  • P2161 [SHOI2009]会场预约 题解
    没事打了个Splay,然后调了3h。觉得题解的找前驱后继与删除复杂了点,主要讲一下这的思路。由于平衡树中每一个点代表的区间互不相交,所有平衡树满足\(l,r\)两个值的BST。......
  • P3224 [HNOI2012]永无乡 题解
    典型Splay练习题。开始建\(n\)个Splay,每一次建边用并查集判断是否在一个子图,不在就合并,即把一个Splay的所有点全插入到另一个Splay中,需要合并的点可以用vector存储。但......
  • P1763 埃及分数 题解
    做完后发现很多题解都是有些细节问题的,对于向上与向下取整非常混乱。第一次做迭代加深搜索的题,记录一下。所谓迭代加深搜索,就是在求搜索树的深度的问题中,枚举层数,取最优......
  • P4048 [JSOI2010]冷冻波 题解
    首先很好想到我们应该预处理出来每一个巫妖王能攻击到的精灵。那么这就是一个几何题。对于每一组精灵与巫妖王,设巫妖王坐标为\((x_1,y_1)\),精灵坐标为\((x_2,y_2)\)。......
  • P7221 [JSOI2010] 蔬菜庆典 题解
    本题解在求无解的情况下优化了下。通过分析样例,我们可以发现如果一个节点有多个Dlihc,那么这些Dlihc对应的权值必须一样,否则可以无限延伸下去。因为一号节点没有Tnera......