zoukankan      html  css  js  c++  java
  • UR 2 密码锁

    $$UR 2 密码锁$$

    • (myy)的题
    • 然而考场上只会(O(3^N))的算法还因为时间问题只写了(O(3^N*N))然后就被(32)位机卡常了只有(26)分,还搞得另外两个题没检查都爆零了,myy:你们是我见过最差的一届
    • 自己垃圾无话可说
    • 部分分可以参考竞赛图求(SCC)的那种套路来做,就是先设一个什么(f[s])表示集合(s)(SCC)的概率,然后用一个类似背包的东西枚举子集来转移(其实这个做法有点强行套上一道以前的题没有考虑本题的性质
    • 然而有更加优美的(O(2^N*N^2))的做法,能够发现竞赛图缩点后一定形如一条链,考虑对链上的每一条边计数,为了方便可以假设最后一个点之后也会连着一条链,所以我们可以(O(2^N))枚举链的前缀,然后这个前缀要全部向剩余的点连边,算一下概率,最后加在一起
    • 考虑进一步优化上面的算法,发现我们一个大小为(M)的没有特殊的边的集合成为一个前缀的概率是((frac{1}{2})^{M*(N-M)}),每加入一条特殊的边就是乘上(2p),考虑对大小为M的集合一起处理,最后一起乘以((frac{1}{2})^{M*(N-M)})就好了,然后其实我并不会第(4)(SubTask)就直接讲正解吧,边的贡献可以直接枚举连通块来算,然后把每些边组成的联通块先用(dp)算出来在取这个集合时边的贡献,然后用背包合在一起,最后一起乘上那个(2)的多少次幂即可,因为边数很小,所以连通块的点数也很小,可以通过
    #include <bits/stdc++.h>
    
    typedef long long LL;
    
    inline int read(int Num = 0, int Flag = 1)
    {
    	char ch = getchar();
    	for (; !isdigit(ch); ch = getchar()) 
    		if (ch == '-')
    			Flag = -1;
    	for (;  isdigit(ch); ch = getchar())
    		Num = Num * 10 + ch - '0';
    	return Num *= Flag;
    }
    
    template <typename T> bool chkmax(T &a, T b) { return a < b? a = b, true : false; }
    template <typename T> bool chkmin(T &a, T b) { return a > b? a = b, true : false; }
    
    const int MAXN = 50 + 5;
    const int mod = 998244353;
    const int base = 1e4;
    
    int fpm(int x, int e)
    {
    	int ret = 1;
    	for (; e; e >>= 1) {
    		if (e & 1) 
    			ret = (LL)ret * x % mod;
    		x = (LL)x * x % mod;
    	}
    	return ret;
    }
    
    int N, M, mul_num;
    
    std::vector<int> G[MAXN];
    int from[MAXN], to[MAXN], p[MAXN];
    
    int size, tot;
    int id[MAXN], occur[MAXN];
    
    void DFS_work(int u, int tot)
    {
    	id[u] = size ++;
    	occur[u] = tot;
    	for (int i = 0; i < (int)G[u].size(); ++i) {
    		int v = G[u][i];
    		if (id[v] < 0) DFS_work(v, tot);
    	}
    }
    
    int sum[MAXN][MAXN];
    int dp[MAXN][MAXN];
    
    int main()
    {
    	freopen("random.in", "r", stdin);
    	freopen("random.out", "w", stdout);
    
    	N = read(), M = read();
    	mul_num = fpm(base, N * (N-1));
    
    	for (int i = 1; i <= M; ++i) {
    		int u = read(), v = read(), w = read();
    
    		G[u].push_back(v);
    		G[v].push_back(u);
    		from[i] = u, to[i] = v;
    		p[i] = (LL)w * fpm(base, mod - 2) % mod;
    	}
    
    	int allblock_size = 0;
    
    	memset(id, -1, sizeof id);
    	for (int i = 1; i <= N; ++i) {
    		if (id[i] >= 0) continue;
    
    		size = 0;
    		tot ++;
    		DFS_work(i, tot);
    		allblock_size += size;
    
    		for (int s = 0; s < 1<<size; ++s) {
    			int coe = 1;
    			for (int j = 1; j <= M; ++j) {
    				if (occur[from[j]] == tot && (((s >> id[from[j]]) ^ (s >> id[to[j]])) & 1)) {
    					coe = coe * 2 % mod;
    					coe = (LL)coe * ((s >> id[from[j]]) & 1? p[j] : mod + 1 - p[j]) % mod;
    				}
    			}
    			int bit = __builtin_popcount(s);
    			sum[tot][bit] = (sum[tot][bit] + coe) % mod;
    		}
    	}
    
    	dp[0][0] = 1;
    	for (int i = 0; i < tot; ++i) {
    		for (int j = 0; j <= N; ++j) {
    			for (int k = 0; k + j <= N; ++k)
    				dp[i+1][j+k] = (dp[i+1][j+k] + (LL)dp[i][j] * sum[i + 1][k] % mod) % mod;
    		}
    	}
    
    	int ans = 0;
    
    	assert(allblock_size == N);
    	for (int i = 1; i <= N; ++i) {
    		ans = (ans + (LL)dp[tot][i] * fpm((mod + 1) / 2, i * (N-i)) % mod) % mod;
    	}
    
    	printf("%lld
    ", (LL)ans * mul_num % mod);
    
    	return 0;
    }
    
    
  • 相关阅读:
    Debian 9 更换源
    MySqlDataAdapter.Fill() 报异常‘给定关键字不在字典中’的解决方案
    阿里云函数计算 .NET Core 初体验
    TimeSpan 的 Milliseconds 和 TotalMilliseconds 有啥区别?
    使用 gitee 托管你的 go 模块
    markdown的css样式(自己写的)
    markdown的流程图实现和代码语法着色
    Python元组与字典详解
    centos7的防火墙(firewalld)
    centos7 安装java和tomcat9
  • 原文地址:https://www.cnblogs.com/pbvrvnq/p/8763202.html
Copyright © 2011-2022 走看看