随机化+矩阵树定理
好神的做法(官方题解)。。。怒膜jry
一直陷在基尔霍夫矩阵的坑里,以为能通过一些数学技巧反推出合理地矩阵?真是naive。
一个重要的思路是:一张图的生成树个数等于把桥边割掉之后剩下的所有子图的生成树个数之积。 这是显然的,毕竟桥边不会贡献答案,而环的贡献可以分开考虑。
发现这玩意儿可以乘之后就可以随机化乱搞了。随机出1000个n=12的小连通块,则生成树个数为 [0,1210] , 最大值远大于998244353。在模意义下等价于均匀分布随机数。再暴力枚举四个块(双搜),判断它们接起来答案是不是k即可。证明详见官方题解。
突然忘记多维指针怎么用,于是代码常数突然变大。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MOD 998244353
#define HMOD 3333331
using namespace std;
namespace runzhe2000
{
typedef unsigned uint;
typedef long long ll;
uint randint()// WC2017式随机数
{
static uint R = 1;
R ^= (R << 13);
R ^= (R >> 17);
R ^= (R << 5);
return R;
}
int rand01(double p)
{
return randint() % MOD < (MOD * p);
}
int abs(int a){return a<0?-a:a;}
const int t = 1000, n = 12;
struct node{int next, val, id;}no[t*t*2];
int d[t+5][n+5][n+5], f[t+5], head[HMOD], hcnt, dd[n+5][n+5];
int hash_find(int val)
{
int hash = val % HMOD;
for(int i = head[hash]; i; i = no[i].next)
if(no[i].val == val) return no[i].id;
return 0;
}
void hash_ins(int val, int id)
{
int hash = val % HMOD;
no[++hcnt] = (node){head[hash], val, id};
head[hash] = hcnt;
}
int fpow(int a, int b)
{
int r = 1;
for(; b; b>>=1)
{
if(b&1)r=(ll)r*a%MOD;
a=(ll)a*a%MOD;
}
return r;
}
int calc(int id)
{
int det = 1, nn = n-1;
for(int i = 1; i <= nn; i++)
for(int j = 1; j <= nn; j++)
dd[i][j] = d[id][i][j];
for(int i = 1; i <= nn; i++)
{
int p = i;
for(int j = i+1; j <= nn; j++) if(abs(dd[j][i]) > abs(dd[p][i])) p = j;
if(p != i){for(int j = 1; j <= nn; j++) swap(dd[i][j], dd[p][j]);det*=-1;}
if(!dd[i][i]) return 0;
int inv = fpow(dd[i][i], MOD - 2);
for(int j = i+1; j <= nn; j++)
{
int base = (ll)dd[j][i] * inv % MOD;
for(int k = i; k <= nn; k++)
dd[j][k] = (dd[j][k] - (ll)dd[i][k] * base % MOD) % MOD;
}
det = (ll) det * dd[i][i] % MOD;
}
return (det + MOD ) % MOD;
}
int out[n*n*4][2], m;
void print(int id, int base)
{
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++)
if(d[id][i][j]) ++m, out[m][0] = i+base, out[m][1] = j+base;
}
void print_make()
{
printf(" %d\n",m);
for(int i = 1; i <= m; i++) printf("%d %d\n",out[i][0], out[i][1]);
m = 0;
}
void solve(int K)
{
for(int i = 1; i <= t; i++) if(f[i])
for(int j = i+1; j <= t; j++) if(f[j])
{
int val = (ll) f[i] * f[j] % MOD, tmp;
if((tmp = hash_find((ll)K * fpow(val, MOD - 2) % MOD)))
{
int k = tmp / 2333, l = tmp % 2333;
printf("%d",4*n);
print(i, 0); print(j, n); print(k, n*2); print(l, n*3);
out[++m][0] = 1; out[m][1] = n+1;
out[++m][0] = n+1; out[m][1] = 2*n+1;
out[++m][0] = 2*n+1; out[m][1] = 3*n+1;
print_make();
return;
}
hash_ins(val, i*2333+j);
}
}
void main()
{
for(int i = 1; i <= t; i++)
{
for(int j = 1; j <= n; j++)
for(int k = j+1; k <= n; k++)
if((d[i][j][k] = -rand01(0.5)))
{
d[i][k][j] = -1;
d[i][j][j]++, d[i][k][k]++;
}
f[i] = (calc(i) + MOD) % MOD;
}
int T, k; scanf("%d",&T);
for(; T--; )
{
scanf("%d",&k);
if(!k) {puts("2 0");continue;}
memset(head,0,sizeof(head));
hcnt = 0;
solve(k);
}
}
}
int main()
{
runzhe2000::main();
}