没智商了
变式可见:【构造 思维题】7.12道路建设
当你自信满满地把你认为的正确密码输入后,时光机滴滴报警 —— 密码错误。你摊坐在了地上。
黑衣人满意地拍了拍你的肩膀:“小伙子,不错嘛。虽然没解开密码,但是离解开密码也不远咯。其实刚才是个对你的考验。来加入我们保护星期日委员会吧!”
你惊讶得从地上直接 splay 了起来:“就是那个传说中的保护星期日委员会?咦,那为什么今天星期日还要上学呢?”
黑衣人:“你不是高三学生吗?”,你一脸无语。黑衣人又说道:“小伙子跟我干吧,我们一起去摧毁 IIIS。”
于是你们跋山涉水来到了 IIIS 基地的一个秘密仓库门前。duang地一下放倒了门卫之后你们试图开门窃取秘密资料。可是这个门装备了最新的智商锁,只有你的智商恰好等于锁的主人门才会打开。于是门上出现一道迷题:
给你一个 k,请你构造一个结点数不超过 100 的无向图,使得这个无向图中生成树的个数对 $998244353$(7×17×223+1,一个质数)取模后恰为 k。
作为智商超高的你一定一眼秒掉了此题!请写一个程序证明你的智商跟智商锁的主人一样吧!
输入格式
第一行一个正整数 T。
接下来 T 行每行一个非负整数 k,保证 $0≤k<998244353$。
输出格式
你需要对每一个给出来的 $k$,输出一张无自环无重边的无向图。如果有多解输出任意一组解均可。如果无解请输出卖萌表情 “QwQ”(不含引号)。
输出无向图时,先一行两个非负整数 $n,m$,分别表示结点数和边数。你需要满足 $1≤n≤100$。接下来 m 行,每行两个整数 v,u 表示 v 号结点和 u 号结点之间有一条无向边。结点从 1 到 n 编号。
题目分析
随机个1000张图,由于一张点数为$n$的完全图生成树个数为$g_i=n^{n-2}$,那么当$nge 10$左右时候$g_i$就可看作在模$p$意义下的一个均匀分布的随机数.
考虑如何由两张图得到一个更大的答案:因为两张图之间本没有边相连,那如果任连一条桥边,就意味着新图的生成树必定要包括这条桥.所以两张图$f,g$可以得到一张新图$h=f imes g$.
接下去就是乱搞用meet in middle找出四元组使得其乘积在模$p$下为$K$.
因为生成的图的生成树数量可视作均匀分布的随机数,所以选四个得到$K$的概率是很大的。
最后还是挂个吉利题解吧……
接下来我们来考虑一种合理的构造方法,直接构造是比较困难的,因为我们现在只能对答案进行乘法(见第七个点)而无法进行加法。于是我们就可以开始尝试乱搞。
标程的做法是:随机1000张点数为12的无向图,用基尔霍夫矩阵求出第$i$张图的生成树个数$f_i$。然后对于每一个$K$,我们试图找到一个四元组$(a,b,c,d)$使得$f_a×f_b×f_c×f_dequiv K pmod{998244353}$,这个显然是可以meet in middle的,我们预处理它们两两的乘积与乘积的逆元,借助hash表就可以在约$10^6$级别的运算量下找到一个合法的四元组。
这个做法相当于自行对最终的图添加了一个比较严苛的条件进行求解,为什么这样是靠谱的呢?接下来提供一种对正确性大致的解释(以下是数学渣JRY在没有任何道理的情况进行的口胡):
首先我们随机无向图的方法是每一条边生成的概率都是$0.8$,而12个点的完全图的生成树个数是$12^{10}$,这是远大于模数的,所以我们可以近似地把$fi$看做均匀分布的随机数(这个和我们平时写的哈希表的思想类似)。接下来我们把这$1000$个随机数的集合四次方再模上模数,这样我们就得到了$10^{12}$个数,和刚才同理,我们也可以把这$10^{12}$个数给近似地看成均匀分布的随机数。
接下来我们要求的是$10^{12}$个小于等于$10^9$的随机数完全覆盖0到$10^9$之间所有数的概率是多少。首先一个数被覆盖的概率是$1-(1-frac{1}{10^9})^{10^{12}} approx 1-mathrm{e}^{-10^3}$,我们可以近似地认为所有数都被覆盖的概率是$(1-mathrm{e}^{-10^3})^{10^9}$,实际上这个数是非常接近$1$的(因为$e^{10^3}$比$10^9$大到哪里去都不知道了)
话说我生成图的方式好像有点奇怪?
1 #include<bits/stdc++.h> 2 #define MO 998244353 3 const int maxn = 54; 4 const int maxm = 20003; 5 6 int qmi(int a, int b) 7 { 8 int ret = 1; 9 for (; b; b>>=1,a=1ll*a*a%MO) 10 if (b&1) ret = 1ll*ret*a%MO; 11 return ret; 12 } 13 std::set<int> gcnt; 14 struct Graph 15 { 16 int n,m,val; 17 int fa[maxn],f[maxn][maxn],deg[maxn]; 18 bool mp[maxn][maxn]; 19 20 Graph(){memset(mp, 0, sizeof mp);} 21 int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} 22 int Gauss(int n) 23 { 24 int ret = 1; 25 for (int i=1; i<=n; i++) 26 { 27 if (!f[i][i]) return 0; 28 for (int j=i+1; j<=n; j++) 29 if (f[j][i]){ 30 int det = 1ll*f[j][i]*qmi(f[i][i], MO-2)%MO; 31 for (int k=i; k<=n; k++) 32 f[j][k] = (f[j][k]-1ll*f[i][k]*det%MO+MO)%MO; 33 } 34 } 35 for (int i=1; i<=n; i++) 36 ret = 1ll*ret*f[i][i]%MO; 37 return ret; 38 } 39 void output(int det) 40 { 41 for (int p=1; p<=n; p++) 42 for (int t=p+1; t<=n; t++) 43 if (mp[p][t]) printf("%d %d ",p+det,t+det); 44 } 45 void random(int Range) 46 { 47 val = 1; 48 do{ 49 memset(f, 0, sizeof f); 50 memset(mp, 0, sizeof mp); 51 n = rand()%Range+1, m = std::min(n-1+rand()%(n*(n-1)/2-n+10), n*(n-1)/2); 52 for (int i=1; i<=n; i++) fa[i] = i; 53 for (int i=1; i<n; i++) 54 { 55 int u = rand()%n+1, v = rand()%n+1; 56 while (find(u)==find(v)) 57 u = rand()%n+1, v = rand()%n+1; 58 fa[fa[u]] = fa[v]; 59 mp[u][v] = mp[v][u] = true; 60 f[u][v] = f[v][u] = MO-1; 61 ++f[u][u], ++f[v][v]; 62 } 63 for (int i=n; i<=m; i++) 64 { 65 int u = rand()%n+1, v = rand()%n+1; 66 while (mp[u][v]||v==u) 67 u = rand()%n+1, v = rand()%n+1; 68 mp[u][v] = mp[v][u] = true; 69 f[u][v] = f[v][u] = MO-1; 70 ++f[u][u], ++f[v][v]; 71 } 72 for (int i=1; i<=n; i++) 73 for (int j=1; j<=n; j++) 74 if (f[i][j] < 0) f[i][j] += MO; 75 val = Gauss(n-1); 76 }while (gcnt.count(val)); 77 gcnt.insert(val); 78 } 79 }f[1335]; 80 struct node 81 { 82 int idx,idy,val; 83 bool operator < (node a) const 84 { 85 return val < a.val; 86 } 87 }ddc[1000035]; 88 int T,n,tot; 89 bool legal; 90 91 void init() 92 { 93 srand(time(0)); 94 f[1].n = f[1].val = 1, gcnt.insert(1); 95 f[2].n = f[2].val = 1, gcnt.insert(1); 96 f[3].n = f[3].val = 1, gcnt.insert(1); 97 for (int i=4; i<=500; i++) f[i].random(20); 98 for (int i=501; i<=1200; i++) f[i].random(50); 99 for (int i=1; i<=600; i++) 100 for (int j=i+1; j<=600; j++) 101 ddc[++tot].val = 1ll*f[i].val*f[j].val%MO, 102 ddc[tot].idx = i, ddc[tot].idy = j; 103 std::sort(ddc+1, ddc+tot+1); 104 } 105 void output(int id1, int id2, int id3, int id4) 106 { 107 int det = 0; 108 int n1 = f[id1].n, n2 = f[id2].n, n3 = f[id3].n, n4 = f[id4].n; 109 if (n1+n2+n3+n4 > 100) return; 110 printf("%d %d ",n1+n2+n3+n4,f[id1].m+f[id2].m+f[id3].m+f[id4].m+3); 111 f[id1].output(det), det += n1, printf("1 %d ",det+1); 112 f[id2].output(det), det += n2, printf("1 %d ",det+1); 113 f[id3].output(det), det += n3, printf("1 %d ",det+1); 114 f[id4].output(det), det += n4; 115 legal = true; 116 } 117 int main() 118 { 119 init(); 120 for (scanf("%d",&T); T; --T) 121 { 122 scanf("%d",&n), legal = false; 123 if (n==0){ 124 puts("2 0");continue; 125 } 126 for (int i=601; i<=1200&&!legal; i++) 127 for (int j=i+1; j<=1200&&!legal; j++) 128 { 129 int trans = 1ll*n*qmi(1ll*f[i].val*f[j].val%MO, MO-2)%MO; 130 int L = 1, R = tot, pos = -1; 131 for (int mid=(L+R)>>1; L<=R; mid=(L+R)>>1) 132 if (ddc[mid].val <= trans) pos = mid, L = mid+1; 133 else R = mid-1; 134 if (ddc[pos].val==trans) 135 output(ddc[pos].idx, ddc[pos].idy, i, j); 136 } 137 if (!legal) puts("QwQ"); 138 } 139 return 0; 140 }
END