190918 CSP-S 2019模拟

本文介绍了CSP-S 2019模拟比赛的题目,涉及分治、Floyd算法、Dijkstra算法、数学题解法及线性代数在动态规划中的应用。A题通过分治+Floyd解决复杂问题,B题利用线性代数求解宝藏期望步数,C题则是签到题,通过生成函数和分治NTT解决。
摘要由CSDN通过智能技术生成

按照惯例,考完第二天调题(大雾

A

现在博主已知的有两种做法,并实现了其中一种。
第一种是 s t d std std 的做法 我考场算错复杂度了告辞
就是利用分治+ f l o y d floyd floyd ,复杂度 O ( n 3 log ⁡ n ) O(n^3\log n) O(n3logn)
第二种方法 f r o m   J u n from\ Jun from Jun ,意思是枚举每个点跑一次 d i j k s t r a dijkstra dijkstra 然后枚举断边进而枚举环更新答案,注意要记一个路径第二个访问到的点来判环是否合法
我写的是 J u n Jun Jun 的做法。
复杂度 O ( n m log ⁡ n + n 2 log ⁡ n ) O(nm\log n+n^2\log n) O(nmlogn+n2logn) 吊打标算
CODE:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,int> pli;
const int rlen=1<<18|1;
char buf[rlen],*ib=buf,*ob=buf;
#define gc() (((ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin)),ib==ob)?-1:*ib++)
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const ll inf=1e18;
const int N=305;
int n,m,ft[N],pre[N];
vector<pii>e[N];
vector<int>E;
set<pli>S;
ll dis[N],Ans[N],vl[N*N];
bool vis[N];
inline int idx(int a,int b){return (a-1)*n+b-1;}
void dijkstra(int s){
	for(ri i=1;i<=n;++i)dis[i]=inf,vis[i]=0,ft[i]=pre[i]=0;
	S.insert(pli(dis[s]=0,s));
	while(S.size()){
		int x=S.begin()->se;
		S.erase(S.begin());
		vis[x]=1;
		for(ri v,i=e[x].size()-1;~i;--i){
			if(vis[v=e[x][i].fi])continue;
			if(dis[v]>dis[x]+e[x][i].se){
				if(dis[v]!=inf)S.erase(pli(dis[v],v));
				dis[v]=dis[x]+e[x][i].se;
				ft[v]=x^s?ft[x]:v;
				pre[v]=x;
				S.insert(pli(dis[v],v));
			}
		}
	}
	for(ri w,u,v,i=E.size()-1;~i;--i){
		u=E[i]/n+1,v=E[i]%n+1,w=vl[E[i]];
		if(pre[u]==v||pre[v]==u||ft[u]==ft[v])continue;
		Ans[s]=min(Ans[s],dis[u]+w+dis[v]);
	}
}
int main(){
	#ifdef ldxcaicai
	freopen("lx.in","r",stdin);
	#endif
	n=read(),m=read();
	for(ri i=1;i<=n;++i)Ans[i]=inf;
	for(ri i=1;i<=n*n;++i)vl[i]=inf;
	for(ri u,v,w,p,i=1;i<=m;++i){
		u=read(),v=read(),w=read();
		if(u==v){
			Ans[u]=min(Ans[u],(ll)w);
			continue;
		}
		if(u>v)swap(u,v);
		p=idx(u,v);
		E.pb(p);
		if(vl[p]!=inf){
			Ans[u]=min(Ans[u],vl[p]+w);
			Ans[v]=min(Ans[v],vl[p]+w);
		}
		if(w<vl[p])vl[p]=w;
		e[u].pb(pii(v,w));
		e[v].pb(pii(u,w));
	}
	sort(E.begin(),E.end());
	E.erase(unique(E.begin(),E.end()),E.end());
	for(ri i=1;i<=n;++i)dijkstra(i);
	for(ri i=1;i<=n;++i)cout<<(Ans[i]==inf?-1:Ans[i])<<' ';
	return 0;
}

B

一道比较妙的数学题,利用一点简单线代知识可以很简单的实现。
然而为啥又是类欧
首先如果 ( n , k ) ≠ 1 (n,k)\not=1 (n,k)=1 可以先同除 g c d gcd gcd
E i E_i Ei 表示宝藏在 i i i 号洞穴的期望步数,然后 A n s = ∑ i = 1 n E i n Ans=\frac{\sum\limits_{i=1}^nE_i}n Ans=ni=1nEi
于是问题转化为求 ∑ i = 1 n E i \sum\limits_{i=1}^nE_i i=1nEi
考虑递推 E E E 数组,现在有两种转移方式:
∀ k < i ≤ n , E i = E i − k + 1 \forall k<i\le n,E_i=E_{i-k}+1 k<in,Ei=Eik+1
∀ 1 ≤ i ≤ k , E i = p + ( 1 − p ) ( E i + n − k + 1 ) = ( 1 − p ) E i + n − k + 1 \forall 1\le i\le k,E_i=p+(1-p)(E_{i+n-k}+1)=(1-p)E_{i+n-k}+1 1ik,Ei=p+(1p)(Ei+nk+1)=(1p)Ei+nk+1
由于 gcd ⁡ ( n , k ) = 1 \gcd(n,k)=1 gcd(n,k)=1 ,所有 E i E_i Ei 的转移构成了一个环,可以设 E 1 = x E_1=x E1=x ,然后推出 E i = a x + b E_i=ax+b Ei=ax+b ,最后利用 E 1 = ( 1 − p ) E 1 + n − k + 1 E_1=(1-p)E_{1+n-k}+1 E1=(1p)E1+nk+1 这个等式解出 x x x 从而求出 ∑ E i \sum E_i Ei ,考试时性价比较高 然而我全场没读懂题可还行
考虑到只需求出 ∑ E i \sum E_i Ei 而不是 E i E_i Ei ,那么 ∀ 1 ≤ i ≤ k , ∑ i + t k ≤ n , t ≥ 0 E i + t k \forall 1\le i\le k,\sum\limits_{i+tk\le n,t\ge0} E_{i+tk} 1ik,i+tkn,t0Ei+tk 就可以表示为 A E i + B AE_i+B AEi+B 的形式
假设 E i ′ = ∑ i + t k ≤ n , t ≥ 0 E i + t k E'_i=\sum\limits_{i+tk\le n,t\ge0} E_{i+tk} Ei=i+tkn,t0Ei+tk ,那么 ∑ i = 1 n E i = ∑ i = 1 k E i ′ \sum\limits_{i=1}^nE_i=\sum\limits_{i=1}^kE'_i i=1nEi=i=1kEi ,相当于变成了一个子问题,其中 n ′ = k , k ′ = n % k n'=k,k'=n\%k n=k,k=n%k,那么考虑新的转移系数即可,思考过程如下:
先定义变换 ( a , b ) ( x ) = a × x + b (a,b)(x)=a\times x+b (a,b)(x)=a×x+b ,显然这个玩意儿可以进行复合
另一方面,考虑从线性代数的角度来看待这个东西,发现可以进行如下的构造:
r e s = ( x 1 0 ) ∗ ( a b 1 ) res= \left( \begin{matrix} x&1&0 \end{matrix} \right) * \left( \begin{matrix} a\\ b\\ 1\\ \end{matrix} \right) res=(x10)ab1
这有啥用?
显然对于一个位置 i i i,有: E i = 1 × E i + 0 E_i=1\times E_i+0 Ei=1×Ei+0
则有:
r e s = ( E i 1 0 ) ∗ ( 1 0 1 ) res= \left( \begin{matrix} E_i&1&0 \end{matrix} \right) * \left( \begin{matrix} 1\\ 0\\ 1\\ \end{matrix} \right) res=(Ei10)101
现在要构造一个转移 E j = A ∗ E i + B E_j=A*E_i+B Ej=AEi+B
只需对后面的
( 1 0 1 ) \left( \begin{matrix} 1\\ 0\\ 1\\ \end{matrix} \right) 101
进行操作,给它乘上这么一个矩阵:
( a 0 0 0 a b 0 0 1 ) \left( \begin{matrix} a&0&0\\ 0&a&b\\ 0&0&1 \end{matrix} \right) a000a00b1
即可,更进一步的,只需要维护出上面这种 3 × 3 3\times 3 3×3 矩阵的乘积就能够维护出答案
假设从 E i − k → E i E_{i-k}\rightarrow E_i EikEi 的转移矩阵是 A A A ,从 E i + n − k → E i E_{i+n-k}\rightarrow E_i Ei+nkEi 的转移矩阵是 B B B ,现在考虑如何求
A ′ = E i − k ′ ′ → E i ′ , B ′ = E i + n ′ − k ′ ′ → E i ′ A'=E'_{i-k'}\rightarrow E'_{i},B'=E'_{i+n'-k'}\rightarrow E'_{i} A=EikEi,B=Ei+nkEi
A ′ : A': A:
i − k ′ = i − ( n − ⌊ n k ⌋ ∗ k ) = i − ( n − k ) + ⌊ n k ⌋ ∗ ( k − 1 ) ⇒ A ′ = A − ⌊ n k ⌋ + 1 B − 1 \begin{aligned}i-k'=&i-(n-\lfloor\frac nk\rfloor*k)\\=&i-(n-k)+\lfloor\frac nk\rfloor*(k-1)\\\Rightarrow A'=&A^{-\lfloor\frac nk\rfloor+1}B^{-1}\end{aligned} ik==A=i(nknk)i(nk)+kn(k1)Akn+1B1
B ′ : B': B:
i + n ′ − k ′ = i + k − ( n − ⌊ n k ⌋ ∗ k ) = i − ( n − k ) + ⌊ n k ⌋ ∗ ( k − 1 ) ⇒ B ′ = A − ⌊ n k ⌋ B − 1 \begin{aligned}i+n'-k'=&i+k-(n-\lfloor\frac nk\rfloor*k)\\=&i-(n-k)+\lfloor\frac nk\rfloor*(k-1)\\\Rightarrow B'=&A^{-\lfloor\frac nk\rfloor}B^{-1}\end{aligned} i+nk==B=i+k(nknk)i(nk)+kn(k1)AknB1
这样就求出了子问题中的转移矩阵,而由于进行了压缩操作,所以 1 ≤ i ≤ k 1\le i\le k 1ik 中每个 i i i 的贡献都发生了变化,我们设原本 1 ≤ i ≤ k 1\le i\le k 1ik 的贡献系数是 S 0 S_0 S0 ,原本 k + 1 ≤ i ≤ n k+1\le i\le n k+1in 的贡献系数是 S 1 S_1 S1 ,显然在压缩过程中这两个值会发生变化:
S 0 ′ = S 0 + S 1 ∗ A + S 1 ∗ A 2 + ⋯ + S 1 ∗ A ⌊ n k ⌋ S_0'=S_0+S_1*A+S_1*A^2+\cdots+S_1*A^{\lfloor\frac nk\rfloor} S0=S0+S1A+S1A2++S1Akn
S 1 ′ = S 0 + S 1 ∗ A + S 1 ∗ A 2 + ⋯ + S 1 ∗ A ⌊ n k ⌋ − 1 S_1'=S_0+S_1*A+S_1*A^2+\cdots+S_1*A^{\lfloor\frac nk\rfloor-1} S1=S0+S1A+S1A2++S1Akn1
这样只需要再维护一个矩阵的等比数列求和即可
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,int> pli;
const int rlen=1<<18|1;
char buf[rlen],*ib=buf,*ob=buf;
#define gc() (((ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin)),ib==ob)?-1:*ib++)
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int mod=1e9+7;
inline int add(int a,int b){return (a+=b)<mod?a:a-mod;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void Add(int&a,int b){(a+=b)<mod?a:(a-=mod);}
inline void Dec(int&a,int b){(a-=b)<0?(a+=mod):a;}
inline void Mul(int&a,int b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,Mul(a,a))(p&1)&&(Mul(ret,a),1);return ret;}
int n,k,p;
inline int gcd(int a,int b){int t;while(b)t=a,a=b,b=t-t/a*a;return a;}
struct Mat{
	int a[3][3];
	Mat(){memset(a,0,sizeof(a));}
	friend inline Mat operator+(Mat a,Mat b){
		for(ri i=0;i<3;++i)for(ri j=0;j<3;++j)Add(a.a[i][j],b.a[i][j]);
		return a;
	}
	friend inline Mat operator*(Mat a,Mat b){
		Mat c;
		for(ri i=0;i<3;++i)for(ri k=0;k<3;++k)if(a.a[i][k])
		for(ri j=0;j<3;++j)if(b.a[k][j])Add(c.a[i][j],mul(a.a[i][k],b.a[k][j]));
		return c;
	}
	friend inline Mat operator*(Mat a,int b){for(ri i=0;i<3;++i)for(ri j=0;j<3;++j)(a.a[i][j])&&(Mul(a.a[i][j],b),1);return a;}
	friend inline Mat operator^(Mat a,int p){
		if(!p){
			Mat ret;
			for(ri i=0;i<3;++i)ret.a[i][i]=1;
			return ret;
		}
		Mat ret=a;
		for(--p;p;p>>=1,a=a*a)if(p&1)ret=ret*a;
		return ret;
	}
	inline Mat F(int k){
		Mat ori,f;
		if(!k)return ori;
		for(ri i=0;i<3;++i)ori.a[i][i]=1;
		if(k==1)return *this;
		f=F(k>>1);
		f=f*(ori+((*this)^(k>>1)));
		if(k&1)f=f+((*this)^k);
		return f;
	}
	inline int vl(){
		int A=mul(a[0][0],(((ll)a[1][1]*a[2][2]-(ll)a[1][2]*a[2][1])%mod+mod)%mod);
		int B=mul(a[1][0],(((ll)a[0][2]*a[2][1]-(ll)a[0][1]*a[2][2])%mod+mod)%mod);
		int C=mul(a[2][0],(((ll)a[0][1]*a[1][2]-(ll)a[0][2]*a[1][1])%mod+mod)%mod);
		return add(A,add(B,C));
	}
	inline Mat Inv(){
		Mat c;
		c.a[0][0]=(((ll)a[1][1]*a[2][2]-(ll)a[1][2]*a[2][1])%mod+mod)%mod;
		c.a[1][0]=(((ll)a[1][2]*a[2][0]-(ll)a[1][0]*a[2][2])%mod+mod)%mod;
		c.a[2][0]=(((ll)a[1][0]*a[2][1]-(ll)a[1][1]*a[2][0])%mod+mod)%mod;
		c.a[0][1]=(((ll)a[0][2]*a[2][1]-(ll)a[0][1]*a[2][2])%mod+mod)%mod;
		c.a[1][1]=(((ll)a[0][0]*a[2][2]-(ll)a[0][2]*a[2][0])%mod+mod)%mod;
		c.a[2][1]=(((ll)a[0][1]*a[2][0]-(ll)a[0][0]*a[2][1])%mod+mod)%mod;
		c.a[0][2]=(((ll)a[0][1]*a[1][2]-(ll)a[0][2]*a[1][1])%mod+mod)%mod;
		c.a[1][2]=(((ll)a[0][2]*a[1][0]-(ll)a[0][0]*a[1][2])%mod+mod)%mod;
		c.a[2][2]=(((ll)a[0][0]*a[1][1]-(ll)a[0][1]*a[1][0])%mod+mod)%mod;
		return c*ksm(vl(),mod-2);
	}
	inline void clear(){memset(a,0,sizeof(a));}
}A,B,F0,F1;
inline int f(int n,int k,Mat s0,Mat s1,Mat a,Mat b){
	if(!k){
		int x=0,y=0,X=0,Y=0;
		x=add(s1.a[0][0],s1.a[0][2]),y=add(s1.a[1][0],s1.a[1][2]);
		X=add(a.a[0][0],a.a[0][2]),Y=add(a.a[1][0],a.a[1][2]);
		return add(mul(x,mul(Y,ksm(dec(1,X),mod-2))),y);
	}
	int t=n/k;
	Mat A,B,ta=a.Inv(),tb=b.Inv();
	A=(ta^(t-1))*tb;
	B=(ta^t)*tb;
	return f(k,n%k,s0+s1*a.F(t),s0+s1*a.F(t-1),A,B);
}
int main(){
	#ifdef ldxcaicai
	freopen("lx.in","r",stdin);
	#endif
	for(ri g,tt=read();tt;--tt){
		n=read(),k=read(),p=read(),g=gcd(n,k),n/=g,k/=g;
		A.clear(),B.clear(),F0.clear(),F1.clear();
		A.a[0][0]=A.a[1][1]=1;
		A.a[1][2]=A.a[2][2]=1;
		B.a[0][0]=B.a[1][1]=dec(1,p);
		B.a[1][2]=B.a[2][2]=1;
		F0.a[0][0]=F0.a[1][1]=F0.a[2][2]=1;
		F1.a[0][0]=F1.a[1][1]=F1.a[2][2]=1;
		cout<<mul(f(n,k,F0,F1,A,B),ksm(n,mod-2))<<'\n';
	}
	return 0;
}

C

签到题,列出每一维的生成函数然后做分治 n t t ntt ntt 即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
#define pb push_back
#define rsz resize
#define sz(x) (int)(x).size()
#define lb lower_bound
#define rb upper_bound
#define all(x) (x).begin(),(x).end()
using namespace std;
const int rlen=1<<18|1;
char buf[rlen],*ib=buf,*ob=buf;
#define gc() (((ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin)),ib==ob)?-1:*ib++)
inline int read(){
	int ans=0;
	char ch=gc();
	bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return f?ans:-ans;
}
const int mod=998244353;
typedef long long ll;
typedef vector<int> poly;
inline int add(int a,int b){return (a+=b)<mod?a:a-mod;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void Add(int&a,int b){(a+=b)<mod?a:(a-=mod);}
inline void Dec(int&a,int b){(a-=b)<0?(a+=mod):a;}
inline void Mul(int&a,int b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,Mul(a,a))if(p&1)Mul(ret,a);return ret;}
int invv[23],w[23],lim,tim;
vector<int>rev[23];
inline void init_ntt(){
	w[22]=ksm(3,(mod-1)>>23);
	for(ri i=21;~i;--i)w[i]=mul(w[i+1],w[i+1]);
	invv[0]=1;
	for(ri i=1,iv=mod+1>>1;i<23;++i)invv[i]=mul(invv[i-1],iv);
}
inline void init(int up){
	lim=1,tim=0;
	while(lim<up)lim<<=1,++tim;
	if(rev[tim].size())return;
	rev[tim].resize(lim);
	for(ri i=0;i<lim;++i)rev[tim][i]=(rev[tim][i>>1]>>1)|((i&1)<<(tim-1));
}
inline void ntt(poly&a,int type){
	for(ri i=0;i<lim;++i)if(i<rev[tim][i])swap(a[rev[tim][i]],a[i]);
	for(ri i=1,t=0,a0,a1;i<lim;i<<=1,++t)for(ri j=0,len=i<<1;j<lim;j+=len)
	for(ri mt=1,k=0;k<i;++k,Mul(mt,w[t]))a0=a[j+k],a1=mul(a[j+k+i],mt),a[j+k]=add(a0,a1),a[j+k+i]=dec(a0,a1);
	if(~type)return;
	reverse(++a.begin(),a.end());
	for(ri i=0;i<lim;++i)Mul(a[i],invv[tim]);
}
inline poly operator*(poly a,poly b){
	int n=a.size(),m=b.size(),t=n+m-1;
	if(t<=128){
		poly c(t);
		for(ri i=0;i<n;++i)for(ri j=0;j<m;++j)Add(c[i+j],mul(a[i],b[j]));
		return c;
	}
	init(t);
	a.resize(lim),ntt(a,1);
	b.resize(lim),ntt(b,1);
	for(ri i=0;i<lim;++i)Mul(a[i],b[i]);
	return ntt(a,-1),a.resize(t),a;
}
const int N=1e5+5;
int n,a[N];
poly res;
inline poly getpoly(int x){
	poly ret;
	if(x>=2)return ret.resize(2),ret[0]=x-2,ret[1]=2,ret;
	return ret.resize(3),ret[2]=1,ret;
}
inline poly solve(int l,int r){
	if(l==r)return getpoly(a[l]);
	int mid=l+r>>1;
	return solve(l,mid)*solve(mid+1,r);
}
int main(){
	int A=mod-1,B=0;
	init_ntt();
	for(ri tt=read();tt;--tt){
		n=read();
		for(ri i=1;i<=n;++i)a[i]=read();
		res=solve(1,n),res.resize(n*2+1);
		for(ri i=0,up=n*2;i<=up;++i)cout<<res[i]<<' ';
		puts("");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值