[省选集训2022] 模拟赛20

星际航道

题目描述

给定一个 \(n\times m\) 的网格图,边有边权,初始边权都是 \(0\),有 \(q\) 次修改,每次修改一条边的边权,问修改后网格图的最小生成树是多少,强制在线。

\(n\times m\leq 10^5,q\leq 2\cdot 10^5\)

解法

重点在于考察网格图的性质,不难发现总边数是 \((n-1)m+n(m-1)\),而最小生成树上的边数是 \(nm-1\),那么没有出现在最小生成树上的边数量是 \(nm-n-m+1\),如果你数感比较好,想到网格图的对偶图点数是 \(nm-n-m+2\),就能发现,网格图最小生成树的补集对应着对偶图的最大生成树

那么我们同时维护最小生成树和最大生成树,修改就分类讨论:

  • 如果是把当前边的权值变小,那么如果这条边在最小生成树中,则不需要修改;如果这条边在最大生成树中,那么尝试把这条边加入最小生成树,把替换下来的边加入最大生成树。
  • 如果是把当前边的权值变大,那么如果这条边在最大生成树中,则不需要修改;如果这条边在最小生成树中,那么尝试把这条边加入最大生成树,把替换下来的边加入最小生成树。

\(\tt lct\) 即可实现支持动态插入的最小生成树,时间复杂度 \(O(q\log nm)\)不想写代码了

星际联邦

题目描述

\(i\in[1,n]\) 可以向 \(p_i\in[0,n]\) 连边,对应的权值是 \(w_{i-j+n\bmod n}\);特别地,如果 \(p_i=0\) 对应的权值是 \(1\),定义生成树的权值为边的权值乘积,问所有可能的生成树权值和。

\(n\leq 2^{20}\)

解法

显然题目是求以 \(0\) 为根的内向生成树的权值和,应用矩阵树定理,我们想要求出下列矩阵的行列式,设 \(s=\sum w_i\)

\[\begin{matrix}s+1 & -w_1 & -w_2 & ... & -w_{n-1}\\-w_{n-1} & s+1 & -w_1 & ... & -w_{n-2}\\-w_{n-2} & -w_{n-1} & s+1 & ... & -w_{n-3}\\... & ... & ... & s+1 & ...\\-w_{1} & -w_2 & -w_3 & ... & s+1\end{matrix} \]

那么就是一个对循环矩阵求行列式的问题,考虑公式:

\[det\begin{bmatrix}a_0 & a_1 & a_2 & ... & a_{n-1}\\a_{n-1} & a_0 & a_1 & ... & a_{n-2}\\a_{n-2} & a_{n-1} & a_0 & ... & a_{n-3}\\... & ... & ... & a_0 & ...\\a_{1} & a_2 & a_3 & ... & a_0\end{bmatrix}=\prod_{i=0}^{n-1} f(w^i_n) \]

其中 \(f(x)=\sum_{i=0}^{n-1} a_i\cdot x^i\),那么可以一次 \(\tt NTT\) 计算出来,时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 1<<20;
#define int long long
const int MOD = 998244353;
int read()
{
    int x=0,f=1;char c;
    while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
int n,ans,p[M],f[M],rev[M];
int qkpow(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=r*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return r;
}
void NTT(int *a,int len,int op)
{
	for(int i=0;i<len;i++)
	{
		rev[i]=(rev[i>>1]>>1)|((i&1)*(len/2));
		if(i<rev[i]) swap(a[i],a[rev[i]]); 
	}
	for(int s=2;s<=len;s<<=1)
	{
		int w=(op==1)?qkpow(3,(MOD-1)/s):
			qkpow(3,MOD-1-(MOD-1)/s);
		for(int i=0,t=s/2;i<len;i+=s)
			for(int j=0,x=1;j<t;j++,x=x*w%MOD)
			{
				int fe=a[i+j],fo=a[i+j+t];
				a[i+j]=(fe+x*fo)%MOD;
				a[i+j+t]=(fe-x*fo%MOD+MOD)%MOD;
			}
	}
	if(op==1) return ;
	int inv=qkpow(len,MOD-2);
	for(int i=0;i<len;i++)
		a[i]=a[i]*inv%MOD;
}
signed main()
{
	freopen("federation.in","r",stdin);
	freopen("federation.out","w",stdout);
	n=1<<read();
	for(int i=1;i<n;i++) p[0]+=p[i]=read();
	f[0]=(p[0]+1)%MOD;
	for(int i=1;i<n;i++) f[i]=(MOD-p[i])%MOD;
	NTT(f,n,1);ans=1;
	for(int i=0;i<n;i++) ans=ans*f[i]%MOD;
	printf("%lld\n",ans);
}
posted @ 2022-04-07 16:43  C202044zxy  阅读(158)  评论(0编辑  收藏  举报