按照惯例,考完第二天调题(大雾
A
现在博主已知的有两种做法,并实现了其中一种。
第一种是 stdstdstd 的做法 我考场算错复杂度了告辞
就是利用分治+ floydfloydfloyd ,复杂度 O(n3logn)O(n^3\log n)O(n3logn)
第二种方法 from Junfrom\ Junfrom Jun ,意思是枚举每个点跑一次 dijkstradijkstradijkstra 然后枚举断边进而枚举环更新答案,注意要记一个路径第二个访问到的点来判环是否合法
我写的是 JunJunJun 的做法。
复杂度 O(nmlogn+n2logn)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 可以先同除 gcdgcdgcd
设 EiE_iEi 表示宝藏在 iii 号洞穴的期望步数,然后 Ans=∑i=1nEinAns=\frac{\sum\limits_{i=1}^nE_i}nAns=ni=1∑nEi
于是问题转化为求 ∑i=1nEi\sum\limits_{i=1}^nE_ii=1∑nEi
考虑递推 EEE 数组,现在有两种转移方式:
∀k<i≤n,Ei=Ei−k+1\forall k<i\le n,E_i=E_{i-k}+1∀k<i≤n,Ei=Ei−k+1
∀1≤i≤k,Ei=p+(1−p)(Ei+n−k+1)=(1−p)Ei+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∀1≤i≤k,Ei=p+(1−p)(Ei+n−k+1)=(1−p)Ei+n−k+1
由于 gcd(n,k)=1\gcd(n,k)=1gcd(n,k)=1 ,所有 EiE_iEi 的转移构成了一个环,可以设 E1=xE_1=xE1=x ,然后推出 Ei=ax+bE_i=ax+bEi=ax+b ,最后利用 E1=(1−p)E1+n−k+1E_1=(1-p)E_{1+n-k}+1E1=(1−p)E1+n−k+1 这个等式解出 xxx 从而求出 ∑Ei\sum E_i∑Ei ,考试时性价比较高 然而我全场没读懂题可还行
考虑到只需求出 ∑Ei\sum E_i∑Ei 而不是 EiE_iEi ,那么 ∀1≤i≤k,∑i+tk≤n,t≥0Ei+tk\forall 1\le i\le k,\sum\limits_{i+tk\le n,t\ge0} E_{i+tk}∀1≤i≤k,i+tk≤n,t≥0∑Ei+tk 就可以表示为 AEi+BAE_i+BAEi+B 的形式
假设 Ei′=∑i+tk≤n,t≥0Ei+tkE'_i=\sum\limits_{i+tk\le n,t\ge0} E_{i+tk}Ei′=i+tk≤n,t≥0∑Ei+tk ,那么 ∑i=1nEi=∑i=1kEi′\sum\limits_{i=1}^nE_i=\sum\limits_{i=1}^kE'_ii=1∑nEi=i=1∑kEi′ ,相当于变成了一个子问题,其中 n′=k,k′=n%kn'=k,k'=n\%kn′=k,k′=n%k,那么考虑新的转移系数即可,思考过程如下:
先定义变换 (a,b)(x)=a×x+b(a,b)(x)=a\times x+b(a,b)(x)=a×x+b ,显然这个玩意儿可以进行复合
另一方面,考虑从线性代数的角度来看待这个东西,发现可以进行如下的构造:
res=(x10)∗(ab1)
res=
\left(
\begin{matrix}
x&1&0
\end{matrix}
\right)
*
\left(
\begin{matrix}
a\\
b\\
1\\
\end{matrix}
\right)
res=(x10)∗⎝⎛ab1⎠⎞
这有啥用?
显然对于一个位置 iii,有:Ei=1×Ei+0E_i=1\times E_i+0Ei=1×Ei+0
则有:
res=(Ei10)∗(101)
res=
\left(
\begin{matrix}
E_i&1&0
\end{matrix}
\right)
*
\left(
\begin{matrix}
1\\
0\\
1\\
\end{matrix}
\right)
res=(Ei10)∗⎝⎛101⎠⎞
现在要构造一个转移 Ej=A∗Ei+BE_j=A*E_i+BEj=A∗Ei+B
只需对后面的
(101)
\left(
\begin{matrix}
1\\
0\\
1\\
\end{matrix}
\right)
⎝⎛101⎠⎞
进行操作,给它乘上这么一个矩阵:
(a000ab001)
\left(
\begin{matrix}
a&0&0\\
0&a&b\\
0&0&1
\end{matrix}
\right)
⎝⎛a000a00b1⎠⎞
即可,更进一步的,只需要维护出上面这种 3×33\times 33×3 矩阵的乘积就能够维护出答案
假设从 Ei−k→EiE_{i-k}\rightarrow E_iEi−k→Ei 的转移矩阵是 AAA ,从 Ei+n−k→EiE_{i+n-k}\rightarrow E_iEi+n−k→Ei 的转移矩阵是 BBB ,现在考虑如何求
A′=Ei−k′′→Ei′,B′=Ei+n′−k′′→Ei′A'=E'_{i-k'}\rightarrow E'_{i},B'=E'_{i+n'-k'}\rightarrow E'_{i}A′=Ei−k′′→Ei′,B′=Ei+n′−k′′→Ei′
A′:A':A′:
i−k′=i−(n−⌊nk⌋∗k)=i−(n−k)+⌊nk⌋∗(k−1)⇒A′=A−⌊nk⌋+1B−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}i−k′==⇒A′=i−(n−⌊kn⌋∗k)i−(n−k)+⌊kn⌋∗(k−1)A−⌊kn⌋+1B−1
B′:B':B′:
i+n′−k′=i+k−(n−⌊nk⌋∗k)=i−(n−k)+⌊nk⌋∗(k−1)⇒B′=A−⌊nk⌋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+n′−k′==⇒B′=i+k−(n−⌊kn⌋∗k)i−(n−k)+⌊kn⌋∗(k−1)A−⌊kn⌋B−1
这样就求出了子问题中的转移矩阵,而由于进行了压缩操作,所以 1≤i≤k1\le i\le k1≤i≤k 中每个 iii 的贡献都发生了变化,我们设原本 1≤i≤k1\le i\le k1≤i≤k 的贡献系数是 S0S_0S0 ,原本 k+1≤i≤nk+1\le i\le nk+1≤i≤n 的贡献系数是 S1S_1S1 ,显然在压缩过程中这两个值会发生变化:
S0′=S0+S1∗A+S1∗A2+⋯+S1∗A⌊nk⌋S_0'=S_0+S_1*A+S_1*A^2+\cdots+S_1*A^{\lfloor\frac nk\rfloor}S0′=S0+S1∗A+S1∗A2+⋯+S1∗A⌊kn⌋
S1′=S0+S1∗A+S1∗A2+⋯+S1∗A⌊nk⌋−1S_1'=S_0+S_1*A+S_1*A^2+\cdots+S_1*A^{\lfloor\frac nk\rfloor-1}S1′=S0+S1∗A+S1∗A2+⋯+S1∗A⌊kn⌋−1
这样只需要再维护一个矩阵的等比数列求和即可
代码:
#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
签到题,列出每一维的生成函数然后做分治 nttnttntt 即可。
代码:
#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;
}