1 条题解
-
0
自动搬运
来自洛谷,原作者为

bzy369258147
**搬运于
2025-08-24 21:46:33,当前版本为作者最后更新于2019-03-14 16:48:52,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
本文旨在探讨关于该题网上流传的唯一一个玄学题解的正确性证明,以及关于该题图的性质的分析.
实名反对楼上的题解关于HNOI2011出题人的评价(大雾
以及这题是一道类似 HNOI2018毒瘤 的 好(毒瘤) 题.
也是一个本质上的 提交答案题.
观察题意不难发现这题是给定图求独立集的个数.
然而对一般图而言这是NP的,所以我们猜想这是一个特殊图.
那它是不是:
链? (X)
树? (X)
基环树? (X)
仙人掌? (?)
二分图? (X)
正则图? (X)
弦图? (X)
那是不是出题人就是毒瘤,就是拿一个NP问题当普通题出出来? (X)
通过观察,我们发现这个图,它,至少,是,一个,平面图...
然而并没有什么X用23333
这是我在求出(1 - 2e5)所有边再拓扑排序以后剩下的图的样子:

把里边相交的环展到大环外面就是一个平面图了.
这是我在求出(2e4 - 1e6)所有边再拓扑排序以后剩下的图的样子:

我们发现它甚至是一个沙漠图(仙人掌森林).
也就是说出题人故意安排的部分分其实是有意义的。
具体分3档
30pt(1) : 树形DP
70pt(1 + 3) : 仙人掌DP
100pt(1 + 2 + 3) : 正解.
通过观察发现,原图所有的联通子图中最多比树多3条边,所以这题直接变成了毒瘤的弱化版,所以所谓的玄学做法(毒瘤的部分分做法)自然能过啦.
代码如下:
#include<bits/stdc++.h> using namespace std; const int mod = 1e9 + 7; int PW2[1000005]; const int maxN = 1e6; int n; int num[1000005]; vector<int> to[1000005]; bool vis[1000005]; bool ins[1000005]; int sat[1000005]; vector<int> QE; void dfs_init( int x, int f ) { vis[x] = true; for( auto N : to[x] ) if( N ^ f ) { if( !vis[N] ) dfs_init( N, x ); else { if( !ins[x] ) QE.push_back( x ); if( !ins[N] ) QE.push_back( N ); ins[x] = ins[N] = true; } } } int dp[1000005][2]; int des[1000005]; int pnt = 0; int dfs_dp( int x ) { dp[x][0] = 1; dp[x][1] = PW2[ num[x] ] - 1; des[x] = pnt; for( auto N : to[x] ) if( des[N] ^ pnt ) { dp[x][0] = 1ll * dp[x][0] * dfs_dp( N ) % mod; dp[x][1] = 1ll * dp[x][1] * dp[N][0] % mod; } if( sat[x] == 1 ) dp[x][0] = 0; if( sat[x] == -1 ) dp[x][1] = 0; return ( dp[x][0] + dp[x][1] ) % mod; } bool check() { for( auto P : QE ) for( auto N : to[P] ) { if( sat[P] == 1 and sat[N] == 1 ) return false; } return true; } int query(int x) { QE.clear(); dfs_init( x, x ); int ans = 0; int len = 1 << QE.size(); for( int i = 0; i < len; i ++ ) { for( int j = 0; j < QE.size(); j ++ ) { sat[ QE[j] ] = (i & (1 << j)) ? 1 : -1; } if( check() ) pnt ++, ( ans += dfs_dp( x ) ) %= mod; } for( int i = 0; i < QE.size(); i ++ ) sat[ QE[i] ] = 0; return ans; } int main(){ int n; cin >> n; PW2[0] = 1; for(int i = 1;i <= n;i ++) PW2[i] = PW2[i - 1] * 2 % mod; for(int i = 1;i <= n;i ++) { int x; cin >> x; num[x] ++; } for(int i = 1;i * i <= maxN;i ++) for(int j = i + 1;2 * i * j <= maxN;j ++) { if( j * j > 2 * maxN ) break; int x = j * j - i * i, y = 2 * i * j; if( x > maxN or y > maxN ) continue; if( !num[x] or !num[y] or __gcd( x, y ) != 1 ) continue; to[x].push_back(y); to[y].push_back(x); } int ans = 1; for(int i = 1;i <= maxN;i ++) if( num[i] and !vis[i] ) { ans = 1ll * ans * query(i) % mod; } cout << ( ans - 1 + mod ) % mod; return 0; }拓扑排序的代码:
#include<bits/stdc++.h> using namespace std; int N = 200000; int M = 0; int deg[1000005]; vector<int> to[1000005]; int main(){ for(int i = 2;i <= N;i ++) { for(int j = 1;j <= i;j ++) { if( 1ll * 2 * i * j > N ) break; if( 1ll * i * i - 1ll * j * j > N ) continue; if( __gcd( i, j ) > 1 ) continue; long long u = i * i - j * j; long long v = 2 * i * j; if( __gcd( u, v ) > 1 ) continue; if( u > v ) swap( u, v ); if( u < M or v < M ) continue; deg[u] ++; deg[v] ++; to[u].push_back(v); to[v].push_back(u); //printf( "%d %d\n", u, v ); } } queue<int> Q; for(int i = 1;i <= N;i ++) if( deg[i] == 1 ) Q.push(i); while( !Q.empty() ) { int x = Q.front(); Q.pop(); for( auto N : to[x] ) { deg[N] --; if( deg[N] == 1 ) Q.push( N ); } } for(int i = 1;i <= N;i ++) if( deg[i] > 1 )for( auto E : to[i] ) if( deg[E] > 1 ) printf( "%d %d\n", i, E ); //for(int i = 1;i <= N;i ++) if( deg[i] > 1 ) printf( "%d %d\n", i, deg[i] ); return 0; }
- 1
信息
- ID
- 2286
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者