P3213 [HNOI2011] 勾股定理 题解
P3213 [HNOI2011] 勾股定理 题解
知识点
二进制枚举,树形计数 DP。
分析
连边
对于 \(A^2+B^2=C^2\),有 \(A=m^2-n^2,B=2mn,C=m^2+n^2\)。
于是我们枚举 \(m,n\),则可以在 \(O(\sqrt{H}\ln{H})\) 的调和级数复杂度内求出所有互质勾股数对。
建模
考虑连边,发现会构成很多连通块,这个问题就变成了多个连通块的独立集计数,这是个 NP 问题。
取出其中任意一个连通块,设其边数为 \(m\),点数为 \(n\),\(m-n+1\) 都会是一个很小的数,于是我们可以先在每个联通块中建出深搜树,二进制枚举其中的非树边上的点的状态,现在变成了一个树上独立集计数问题。
实现
法 1
我们直接枚举每个非树边相关的点的状态,时间复杂度为 \(O(\sum_{(m,n)} 4^{m-n+1}(n+m))\)。
法 2
想要进一步优化吗?我们枚举每一条非树边的两端点的状态,发现并不能都选,所以从 \(4\) 个状态减少到了 \(3\) 个,时间复杂度为 \(O(\sum_{(m,n)} 3^{m-n+1}(n+m))\)。
法 3
假设选为 \(1\),不选为 \(0\),那么非树边的两端点状态可以为 \((0,0),(0,1),(1,0)\),其中 \((0,0),(0,1)\) 我们可以在同一次 DP 里一起解决,枚举数变为 \(2\),时间复杂度为 \(O(\sum_{(m,n)} 2^{m-n+1}(n+m))\)。
代码
#define Plus_Cat "gougu"
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~i;y=(g)[(i=(g)[i].nxt)>0?i:0].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(1e6+10);
namespace IOEcat {
//Fast IO...
} using namespace IOEcat;
namespace Modular {
//Modular...
} using namespace Modular;
bool mark[N][2];
int n,m,ans,tot,tim;
int a[N],dep[N],des[N];
int f[N][2];
vector<int> g[N];
struct edge { int u,v; } e[10];
int gcd(int a,int b) { return !b?a:gcd(b,a%b); }
void dfs(int u,int fa) {
dep[u]=dep[fa]+1;
for(int v:g[u])if(v^fa) {
if(dep[v]) {
if(dep[v]<dep[u])e[++tot]= {v,u};
continue;
}
dfs(v,u);
}
}
void DP(int u,int fa) {
des[u]=tim,f[u][0]=!mark[u][1],f[u][1]=(!mark[u][0]?a[u]:0);
for(int v:g[u])if(des[v]!=tim)DP(v,u),tomul(f[u][0],add(f[v][0],f[v][1])),tomul(f[u][1],f[v][0]);
}
void Mark(const int c,const int &rt,int &res) {
if(c>tot)return ++tim,DP(rt,0),toadd(res,f[rt][0],f[rt][1]),void();
const int &u(e[c].u),&v(e[c].v);
const bool mu1(mark[u][1]),mv0(mark[v][0]),mu0(mark[u][0]);
if(!mark[u][1])mark[u][0]=1,Mark(c+1,rt,res),mark[u][0]=mu0;
if(!mark[u][0]&&!mark[v][1])mark[u][1]=mark[v][0]=1,Mark(c+1,rt,res),mark[u][1]=mu1,mark[v][0]=mv0;
}
signed main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
/*DE("Input");*/
I(n);
FOR(i,1,n) {
int h;
I(h),++a[h],tomax(m,h);
}
FOR(i,1,m)if(a[i])a[i]=add(Pow(2,a[i]),Mod-1);
/*DE("Build");*/
for(int i(1); i*i<=m; ++i)for(int j(i+1); 2*i*j<=m&&j*j-i*i<=m; ++j) {
int x(j*j-i*i),y(2*i*j);
if(!a[x]||!a[y]||gcd(x,y)!=1)continue;
g[x].push_back(y),g[y].push_back(x);
}
/*DE("Solve");*/
ans=1;
FOR(rt,1,m)if(a[rt]&&!dep[rt]) {
int res(0);
tot=0,tim=0,dfs(rt,0),Mark(1,rt,res),tomul(ans,res);
if(tot)DE(rt,res);
}
/*DE("Output");*/
O(add(ans,Mod-1),'\n');
return 0;
}

浙公网安备 33010602011771号