翻译都是嫖的。(心虚
\(\cal T_2\) 星际航道
\(\mathbb{Description}\)
给定一个 \(n×m\) 的网格图,边有边权,初始边权都是 \(0\),有 \(q\) 次修改,每次修改一条边的边权,问修改后网格图的最小生成树,强制在线。
\(n×m≤10^5,q≤2⋅10^5\).
\(\mathbb{Solution}\)
\(\text{Subtask 1}\):\(n=2\)
题解说可以动态 \(\mathtt{dp}\),可是我不知道这个玩意怎么 \(\mathtt{dp}\).
\(\text{Subtask 2}\):正解
遇到特殊图一定要想性质!可以发现,这题实际上就是 \(\text{[HNOI 2010] }\)城市建设 的在线版。对于网格图,它的总边数为 \(n\cdot (m-1)+(n-1)\cdot m=2nm-n-m\),包含所有点的最小生成树的边数为 \(nm-1\),那么剩下的边数就是 \(nm-n-m+1\),而这个值恰好是网格图的对偶图形成的生成树的边数!同时我们也能证明网格图的生成树边的补集 \(E'\) 确实能形成对偶图的生成树,这是因为当 \(E'\) 中存在环时,网格图一定不连通。所以我们维护网格图的最小生成树和对偶图的最大生成树:
- 如果是把当前边的权值变小,那么如果这条边在最小生成树中,则不需要修改;如果这条边在最大生成树中,那么尝试把这条边加入最小生成树,把替换下来的边加入最大生成树;
- 如果是把当前边的权值变大,那么如果这条边在最大生成树中,则不需要修改;如果这条边在最小生成树中,那么尝试把这条边加入最大生成树,把替换下来的边加入最小生成树。
用 \(\rm lct\) 即可动态维护。可以发现,"最大生成树" 的存在弥补了短浅的贪心策略,对当前边不优的情况也进行维护,从而在最小生成树变劣时进行复杂度低的更新。
\(\mathbb{Code}\)
对不起。
\(\cal T_3\) 星际联邦
\(\mathbb{Description}\)
给定 \(n\) 个点。点 \(i∈[1,n]\) 可以向 \(p_i∈[0,n]\) 连边,对应的权值是 \(w_{i−j+n\bmod n}\);特别地,如果 \(p_i=0\),对应的权值是 \(1\). 定义生成树的权值为边的权值乘积,问所有可能的生成树权值和。
\(n\le 2^{20}\).
\(\mathbb{Solution}\)
\(\text{Subtask 1}\):\(n\le 256\)
首先发现题意是求内向生成树森林的边权积之和,于是可以用矩阵树定理。一个经典的方法是新建节点 \(n+1\),使 \(n\) 个点均连一条边(边权为 \(1\),对答案无影响)至 \(n+1\),题目就转化成求新图内向生成树边权积之和。此时拉普拉斯矩阵的 \(i\) 行 \(i\) 列(\(i\le n\))就是 \(1+\sum w\),其余赋值不再赘述。高斯消元即可做到 \(\mathcal O(n^3)\).
\(\text{Subtask 2}\):所有 \(w\) 均相等
直接打表。
\(\mathbb{Code}\)
# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; bool f=0; char s;
while(!isdigit(s=getchar())) f|=(s=='-');
for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
return f? -x: x;
}
template <class T>
inline void write(T x) {
static int writ[50], w_tp=0;
if(x<0) putchar('-'), x=-x;
do writ[++w_tp]=x-x/10*10, x/=10; while(x);
while(putchar(writ[w_tp--]^48), w_tp);
}
# include <queue>
using namespace std;
const int maxn = 1e6;
const int mod = 998244353;
inline void dec(int& x,int y) { x = (x-y<0?x-y+mod:x-y); }
inline int inv(int x,int y=mod-2) {
int r=1;
for(; y; y>>=1, x=1ll*x*x%mod)
if(y&1) r=1ll*r*x%mod;
return r;
}
int w[maxn],n,p[50],ans;
namespace Subtask_1 {
int in[50];
void dfs(int x) {
if(x>n) {
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) in[j]=0;
in[i]=1;
for(int j=p[i]; j; j=p[j])
if(in[j]) return;
else in[j]=1;
}
int ret=1;
for(int i=1;i<=n;++i) if(p[i])
ret = 1ll*ret*w[(i-p[i]+n)%n]%mod;
ans = (ans+ret)%mod;
return;
}
for(int i=0;i<=n;++i) {
if(x==i) continue;
p[x]=i; dfs(x+1);
}
}
void work() {
dfs(1);
print(ans,'\n');
}
}
namespace Subtask_2 {
int a[300][300];
int gauss() {
int mul, j, ret=1, Inv; bool f=0;
for(int i=0;i<n;++i) {
for(j=i;j<n;++j) if(a[j][i]) break;
if(j>=n) return 0;
if(i^j) swap(a[i],a[j]), f^=1;
ret = 1ll*ret*a[i][i]%mod;
Inv = inv(a[i][i]);
for(j=i+1;j<n;++j) {
mul = 1ll*a[j][i]*Inv%mod;
for(int k=i;k<n;++k)
dec(a[j][k],1ll*mul*a[i][k]%mod);
}
}
return f? mod-ret: ret;
}
void work() {
int all=1;
for(int i=1;i<n;++i)
all = (all+w[i])%mod;
for(int i=0;i<n;++i) a[i][i] = all;
for(int i=1;i<n;++i)
for(int j=0;j<n;++j)
a[j][(n-i+j)%n] = mod-w[i];
print(gauss(),'\n');
}
}
int main() {
freopen("federation.in","r",stdin);
freopen("federation.out","w",stdout);
n = (1<<read(9));
for(int i=1;i<n;++i) w[i]=read(9);
if(n<=8) Subtask_1::work();
else Subtask_2::work();
return 0;
}