区间积 考虑把 的限制转换成前缀积,然后遗憾地发现
没有逆元,不满足除法
嗯,考虑分别求 的答案,发现对于 2 的每一个方案和 5 的每一个方案,可以还原出10的唯一一个方案,所以10 的答案就是 2,5 的答案的乘积
先考虑没有0的情况,一个 的限制其实是前缀积
的限制
定了一个 ,后面的也就跟着定了,也就是说最后合法的方案数就是可以填的数的联通块的个数次方
发现可以用并查集维护这一个连通块,判合法的话用带权并查集暴力维护一个点是根的几倍就可以了
有0怎么办,考虑,
表示
的答案,枚举第一个 0 的出现位置
,g 表示区间没有一个0的答案,按上述并查集的处理方法可以求出
当前节点作为0,当且仅当跨过它的全部是0,判一下就好
#include<bits/stdc++.h>
#define N 205
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + ch-'0', ch = getchar();
return cnt * f;
}
const int Mod = 1000000007;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int power(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans; }
int n, a[N], Q;
struct data{ int l, r, ans;} q[N];
int b, f[N][N], g[N][N], fa[N], val[N], inv[N];
vector<data> tmp;
int ans;
int find(int x){
if(x == fa[x]) return x;
int t = find(fa[x]);
val[x] = 1ll * val[fa[x]] * val[x] % b;
return fa[x] = t;
}
bool merge(int x, int y, int z){
int fx = find(x), fy = find(y);
if(fx ^ fy){
fa[fx] = fy; val[fx] = 1ll * val[y] * inv[val[x]] * inv[z] % b;
return true;
} else return 1ll * val[y] * inv[val[x]] % b == z;
}
int F(int l, int r, vector<data> v){
int &res = f[l][r];
if(v.empty()) return res = power(b - 1, r - l + 1);
if(~res) return res;
for(int i = l - 1; i <= r; i++) fa[i] = i, val[i] = 1;
for(int i = 0; i < v.size(); i++){
data now = v[i];
if(now.ans == 0) return res = 0;
if(!merge(now.l - 1, now.r, now.ans)) return res = 0;
}
int cnt = 0;
for(int i = l-1; i <= r; i++) if(fa[i] == i) cnt++;
return power(b - 1, cnt - 1);
}
int G(int l, int r, vector<data> v){
int &res = g[l][r];
if(v.empty()) return res = power(b, r - l + 1);
if(~res) return res;
res = F(l, r, v);
for(int i = l; i <= r; i++){
bool flg = 0;
vector<data> L, R;
for(int j = 0; j < v.size(); j++){
data now = v[j];
if(now.l <= i && i <= now.r) if(now.ans != 0) flg = 1;
if(now.r < i) L.push_back(now);
if(now.l > i) R.push_back(now);
}
if(flg) continue;
res = add(res, mul(F(l, i-1, L), G(i+1, r, R)));
} return res;
}
int main(){
n = read(); Q = read();
for(int i = 1; i<= Q; i++){
q[i].l = read() + 1; q[i].r = read() + 1; q[i].ans = read();
}
b = 2;
memset(f, -1, sizeof(f));
memset(g, -1, sizeof(g)); tmp.clear();
for(int i = 1; i <= Q; i++) tmp.push_back((data){q[i].l, q[i].r, q[i].ans % 2});
inv[1] = 1;
ans = G(1, n, tmp);
b = 5;
memset(f, -1, sizeof(f));
memset(g, -1, sizeof(g)); tmp.clear();
for(int i = 1; i <= Q; i++) tmp.push_back((data){q[i].l, q[i].r, q[i].ans % 5});
inv[1] = 1; inv[2] = 3; inv[3] = 2; inv[4] = 4;
ans = mul(ans, G(1, n, tmp));
cout << ans;
return 0;
}
最短路 考场一直在纠结标号的问题,其实每个点是等价的,统计个数就可以了 表示有 i 个点,j 层,最后一层有 k 个点的概率
表示
个点到
个点至少有一条边的概率,
表示不存在这条边的概率
表示
个点到
个点一条边都没有的概率
然后可以枚举最后一个点接到哪一层,算一下贡献
然后一些比我巨得多的神仙就会发现,维护层数是没有必要的
我们令 为 i 个点,最后一层 j 个点的期望层数
也就是说
转移的意义:对于每一种合法情况,层数都多了1,对层数的贡献就是合法情况的概率
换句话说,当前的期望 = 上一层的期望 * 转移概率 + 对当前有贡献的概率 * 转移概率
最大的收获就是在等价的情况下不用考虑标号,考虑个数一起转移
// 50 pts
#include<bits/stdc++.h>
#define N 405
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + ch-'0', ch = getchar();
return cnt * f;
}
int n, p;
const int Mod = 998244353, INF = 1e9;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod;}
void Add(int &a, int b){ a = add(a, b);}
int power(int a, int b){ int ans = 1;
for(;b;b>>=1){ if(b&1) ans = mul(ans, a); a = mul(a, a);}
return ans;
}
int f[N][N][N]; // 有 i 个点,j 层,最后一层有 k 个 的概率
int fac[N], inv[N], pw[N], p1[N][N], p2[N][N];
void prework(){
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= n; i++) fac[i] = mul(fac[i-1], i), inv[i] = mul(Mod-Mod/i, inv[Mod%i]);
for(int i = 2; i <= n; i++) inv[i] = mul(inv[i], inv[i-1]);
pw[0] = 1;
for(int i = 1; i <= n; i++) pw[i] = mul(pw[i-1], p);
for(int i = 1; i <= n; i++)
for(int j = 0; j <= n; j++) p1[i][j] = power(add(1, Mod - pw[i]), j); // 至少联通
for(int i = 1; i <= n; i++)
for(int j = 0; j <= n; j++) p2[i][j] = power(p, i * j); // 不连通
}
int C(int n, int m){ if(n < 0 || m < 0 || n < m) return 0; return mul(fac[n], mul(inv[n-m], inv[m]));}
int main(){
n = read(), p = read(); p = mul(1e6 - p, power(1e6, Mod - 2));
prework();
f[1][1][1] = 1;
for(int i = 1; i <= n-1; i++){
for(int j = 1; j <= i; j++){
for(int k = 1; k <= i; k++){
if(f[i][j][k]){
for(int l = 1; l <= n - i - 1; l++){
int tmp = mul(p1[k][l], mul(p2[k][n-i-l], C(n-i-1, l)));
Add(f[i + l][j + 1][l], mul(f[i][j][k], tmp));
}
}
}
}
}
int ans = 0;
for(int i = 1; i <= n-1; i++){
for(int j = 1; j <= i; j++){
for(int k = 1; k <= i; k++){
if(f[i][j][k]){
Add(ans, mul(j, mul(add(1, Mod - pw[k]), f[i][j][k])));
Add(ans, mul(mul(p2[n-i][k], f[i][j][k]), INF % Mod));
}
}
}
} cout << mul(mul(ans, n-1), power(1e6, n*n)); return 0;
}
// 100 pts
#include<bits/stdc++.h>
#define N 405
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + ch-'0', ch = getchar();
return cnt * f;
}
int n, p;
const int Mod = 998244353, INF = 1e9;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod;}
void Add(int &a, int b){ a = add(a, b);}
int power(int a, int b){ int ans = 1;
for(;b;b>>=1){ if(b&1) ans = mul(ans, a); a = mul(a, a);}
return ans;
}
int f[N][N], e[N][N];
int fac[N], inv[N], pw[N], p1[N][N], p2[N][N];
void prework(){
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= n; i++) fac[i] = mul(fac[i-1], i), inv[i] = mul(Mod-Mod/i, inv[Mod%i]);
for(int i = 2; i <= n; i++) inv[i] = mul(inv[i], inv[i-1]);
pw[0] = 1;
for(int i = 1; i <= n; i++) pw[i] = mul(pw[i-1], p);
for(int i = 1; i <= n; i++)
for(int j = 0; j <= n; j++) p1[i][j] = power(add(1, Mod - pw[i]), j); // 至少联通
for(int i = 1; i <= n; i++)
for(int j = 0; j <= n; j++) p2[i][j] = power(p, i * j); // 不连通
}
int C(int n, int m){ if(n < 0 || m < 0 || n < m) return 0; return mul(fac[n], mul(inv[n-m], inv[m]));}
int main(){
n = read(), p = read(); p = mul(1e6 - p, power(1e6, Mod - 2));
prework();
f[1][1] = 1; e[1][1] = 0;
for(int i = 1; i <= n-1; i++){
for(int j = 1; j <= i; j++){
if(f[i][j]){
for(int k = 1; k <= n - i - 1; k++){
int tmp = mul(p1[j][k], mul(p2[j][n-i-k], C(n-i-1, k)));
Add(f[i + k][k], mul(f[i][j], tmp));
Add(e[i + k][k], mul(add(e[i][j], f[i][j]), tmp));
}
}
}
}
int ans = 0;
for(int i = 1; i <= n-1; i++){
for(int j = 1; j <= i; j++){
if(f[i][j]){
Add(ans, mul(add(1, Mod - pw[j]), add(f[i][j], e[i][j])));
Add(ans, mul(mul(p2[n-i][j], f[i][j]), INF % Mod));
}
}
} cout << mul(mul(ans, n-1), power(1e6, n*n)); return 0;
}
T3:Burnside,咕咕咕