ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

题解-CF468E Permanent

2021-11-04 17:01:17  阅读:145  来源: 互联网

标签:le arrb arra int 题解 CF468E Permanent atot btot


题意

给定一个 \(n \times n\) 的矩阵,其中仅有 \(k\) 个位置的值可能不是 \(1\)。对于第 \(i\) 个位置 \((x_i,y_i)\),其值为 \(w_i\)。要求这个矩阵的积和式,对 \(10^9 + 7\) 取模。

数据范围:\(1 \le n \le 10^5\),\(1 \le k \le 50\),\(1 \le x_i,y_i \le n\),\(0 \le w_i < 998244353\)。

题解

首先把每个 \(w_i\) 拆成 \(1\) 和 \(w_i-1\),也就是把每个特殊位置看做是在 \(1\) 的基础下额外增加了 \(w_i-1\)。

然后枚举我们算积和式的时候用了哪些额外增加的元素,要求这些元素的 \(x_i\) 互不相同,\(y_i\) 也互不相同。

容易想到一个暴力:

对于每一列,枚举额外增加的是哪一行,或是不选择额外增加的,然后状压哪些行已经被占用了。

考虑优化。注意到如果一行在之后的列都不会用到了,那就可以不状压这行。

这样事实上就可以过了,但是这样可能会被卡到 \(\Theta(k^22^{\frac{k}{2}})\)。

注意到如果一行的元素很少,那我们去掉这一行就会很容易。

于是每次找到元素数最少的行,并找到用到这行的列,先枚举这些被用到的列。

这样跑得飞快,在 CF 上是最优解(截至 2021.11.4)。

代码

#include<bits/stdc++.h>
#define L(i, j, k) for (int i = (j); i <= (k); i++)
#define R(i, j, k) for (int i = (j); i >= (k); i--) 
#define sz(a) ((int) (a).size())
#define ll long long 
#define vi vector < int > 
using namespace std;
const int N = 1 << 6, M = 1e5 + 7, mod = 1e9 + 7;
int n, m, a[N][N], fac[M], ns;
int x[N], y[N], w[N];
int arra[N], atot, arrb[N], btot;
int cnta[N], cntb[N];
unordered_map < ll, int > mp[N], cp[N];
int main () {
	ios :: sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n >> m;
	memset(a, -1, sizeof(a));
	fac[0] = 1;
	L(i, 1, n) fac[i] = (ll) fac[i - 1] * i % mod;
	L(i, 1, m) {
		cin >> x[i] >> y[i] >> w[i], (w[i] += mod - 1) %= mod;
		arra[++atot] = x[i];
		arrb[++btot] = y[i];
	}
	sort(arra + 1, arra + atot + 1), atot = unique(arra + 1, arra + atot + 1) - arra - 1;
	sort(arrb + 1, arrb + btot + 1), btot = unique(arrb + 1, arrb + btot + 1) - arrb - 1;
	L(i, 1, m) 
		x[i] = lower_bound (arra + 1, arra + atot + 1, x[i]) - arra, y[i] = lower_bound(arrb + 1, arrb + btot + 1, y[i]) - arrb, 
		a[x[i]][y[i]] = w[i], ++cnta[x[i]], ++cntb[y[i]];
	mp[0][0] += 1;
	while (1) {
		int o = -1;
		L(i, 1, btot) if(cntb[i] && (o == -1 || cntb[o] > cntb[i])) o = i;
		if(o == -1) 
			break ;
		L(i, 1, atot) if(a[i][o] != -1 && cnta[i]) {
			cnta[i] = 0;
			ll cle = ((1LL << btot) - 1) << 1, msk = 0;
			vi S;
			L(j, 1, btot) if(a[i][j] != -1) {
				S.push_back(j);
				cntb[j] -= 1, msk |= 1LL << j;
				if(!cntb[j]) cle ^= 1LL << j;
			}
			L(z, 0, m) 
				for (auto u : mp[z]) {
					(cp[z][u.first & cle] += u.second) %= mod;
					for (const int &t : S) if(!(u.first >> t & 1)) 
						(cp[z + 1][(u.first | (1LL << t)) & cle] += (ll) u.second * a[i][t] % mod) %= mod; 
				}
			L(z, 0, m) swap (mp[z], cp[z]), cp[z].clear ();
		}
	}
	L(z, 0, m) for (auto u : mp[z]) (ns += (ll) u.second * fac[n - z] % mod) %= mod; 
	cout << ns << '\n';
	return 0;
} 

祝大家学习愉快!

标签:le,arrb,arra,int,题解,CF468E,Permanent,atot,btot
来源: https://www.cnblogs.com/zkyJuruo/p/15509016.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有