码迷,mamicode.com
首页 > 编程语言 > 详细

UOJ#75. 【UR #6】智商锁 随机化算法 矩阵树定理

时间:2019-03-17 13:33:58      阅读:206      评论:0      收藏:0      [点我收藏+]

标签:tar   接下来   blog   sdi   假设   cpp   tchar   amp   随机化算法   

原文链接www.cnblogs.com/zhouzhendong/p/UOJ75.html

前言

根本没想到。

题解

首先我们可以考虑一种做法:

找一些图,使得他们各自的生成树个数乘起来等于 k。

那么只要将他们用一条链连起来就得到答案了。

接下来考虑如何得到这些图。

 

考虑随机生成一个 n 个点的图,它的生成树个数最大是 $n^{n-2}$ 。

我们假装一个 n 个点的图的生成树个数是 $[0,n^{n-2}]$ 中的随机数。

假设我们随机生成了 S 个这样的图,如果我们在这 S 个图中随机选择 t 个连起来,那么我们就得到了 $S^t$ 个随机数。

令 n = 12, S = 1000, t = 4,由于 $n^{n-2} = 12^{10}> 62\cdot 998244353$,所以我们可以在对 998244353 取模之后把生成树个数当作一个 $[0,998244353)$ 中的随机数。则我们得到了 $S^t = 10^{12}$ 个随机数。这 $10^{12}$ 个数中不含有 k 个概率非常小。

我们不可能把所有所有的这些随机数都求出来,所以我们用类似于 BSGS 的办法折半一下就好了。

代码

#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define fi first
#define se second
#define _SEED_ (‘C‘+‘L‘+‘Y‘+‘A‘+‘K‘+‘I‘+‘O‘+‘I‘)
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);						For(_v2,L,R)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long LL;
typedef vector <int> vi;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f|=ch==‘-‘,ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=1005,M=20,mod=998244353;
void Add(int &x,int y){
	if ((x+=y)>=mod)
		x-=mod;
}
void Del(int &x,int y){
	if ((x-=y)<0)
		x+=mod;
}
int Pow(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=(LL)x*x%mod)
		if (y&1)
			ans=(LL)ans*x%mod;
	return ans;
}
vector <pair <int,int> > g[N];
int val[N],ival[N];
int calc(int n,vector <pair <int,int> > &e){
	static int a[M][M];
	clr(a);
	for (auto E : e){
		int x=E.fi,y=E.se;
		a[x][y]++,a[y][x]++;
		a[x][x]--,a[y][y]--;
	}
	For(i,1,n)
		For(j,1,n)
			if (a[i][j]<0)
				a[i][j]+=mod;
	n--;
	For(i,1,n){
		For(j,i,n)
			if (a[j][i]!=0){
				For(k,i,n)
					swap(a[i][k],a[j][k]);
				break;
			}
		if (a[i][i]==0)
			return 0;
		For(j,i+1,n){
			int v=(LL)a[j][i]*Pow(a[i][i],mod-2)%mod;
			For(k,i,n)
				Del(a[j][k],(LL)a[i][k]*v%mod);
		}
	}
	int ans=mod-1;
	For(i,1,n)
		ans=(LL)ans*a[i][i]%mod;
	return ans;
}
struct hash_map{
    static const int Ti=233,mod=1<<20;
    int cnt,k[mod+1],v[mod+1],nxt[mod+1],fst[mod+1];
    int Hash(int x){
        int v=x&(mod-1);
        return v==0?mod:v;    
    }
    void clear(){
        cnt=0;
        memset(fst,0,sizeof fst);
    }
    void update(int x,int a){
        int y=Hash(x);
        for (int p=fst[y];p;p=nxt[p])
            if (k[p]==x){
                v[p]=a;
                return;
            }
        k[++cnt]=x,nxt[cnt]=fst[y],fst[y]=cnt,v[cnt]=a;
        return;
    }
    int find(int x){
        int y=Hash(x);
        for (int p=fst[y];p;p=nxt[p])
            if (k[p]==x)
                return v[p];
        return 0;
    }
    int &operator [] (int x){
        int y=Hash(x);
        for (int p=fst[y];p;p=nxt[p])
            if (k[p]==x)
                return v[p];
        k[++cnt]=x,nxt[cnt]=fst[y],fst[y]=cnt;
        return v[cnt]=0;
    }
}Map;
int S=1000;
void prework(){
	srand(_SEED_);
	int n=12;
	For(i,1,S){
		For(j,1,n)
			For(k,1,j-1)
				if (rand()%10>=2)
					g[i].pb(mp(j,k));
		val[i]=calc(n,g[i]);
		ival[i]=Pow(val[i],mod-2);
	}
	Map.clear();
	For(i,1,S)
		For(j,1,i){
			int tmp=(LL)val[i]*val[j]%mod;
			Map[tmp]=i*(S+1)+j;
		}
}
void solve(int x){
	if (!x)
		return (void)puts("2 0");
	For(i,1,S)
		For(j,1,i){
			int tmp=(LL)x*ival[i]%mod*ival[j]%mod;
			if (Map[tmp]){
				int v[4]={i,j,Map[tmp]/(S+1),Map[tmp]%(S+1)};
				vector <pair <int,int> > res;
				res.clear();
				int n=48,cnt=0;
				For (k,0,3){
					if (cnt>0)
						res.pb(mp(cnt,cnt+1));
					for (auto e : g[v[k]])
						res.pb(mp(e.fi+cnt,e.se+cnt));
					cnt+=12;
				}
				printf("%d %d\n",n,(int)res.size());
				for (auto e : res)
					printf("%d %d\n",e.fi,e.se);
				return;
			}
		}
}
int main(){
	prework();
	int T=read();
	while (T--)
		solve(read());
	return 0;
}

  

UOJ#75. 【UR #6】智商锁 随机化算法 矩阵树定理

标签:tar   接下来   blog   sdi   假设   cpp   tchar   amp   随机化算法   

原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ75.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!