按照惯例,考完第二天调题(大雾
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=1∑nEi
于是问题转化为求
∑
i
=
1
n
E
i
\sum\limits_{i=1}^nE_i
i=1∑nEi
考虑递推
E
E
E 数组,现在有两种转移方式:
∀
k
<
i
≤
n
,
E
i
=
E
i
−
k
+
1
\forall k<i\le n,E_i=E_{i-k}+1
∀k<i≤n,Ei=Ei−k+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
∀1≤i≤k,Ei=p+(1−p)(Ei+n−k+1)=(1−p)Ei+n−k+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=(1−p)E1+n−k+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}
∀1≤i≤k,i+tk≤n,t≥0∑Ei+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+tk≤n,t≥0∑Ei+tk ,那么
∑
i
=
1
n
E
i
=
∑
i
=
1
k
E
i
′
\sum\limits_{i=1}^nE_i=\sum\limits_{i=1}^kE'_i
i=1∑nEi=i=1∑kEi′ ,相当于变成了一个子问题,其中
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=A∗Ei+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
Ei−k→Ei 的转移矩阵是
A
A
A ,从
E
i
+
n
−
k
→
E
i
E_{i+n-k}\rightarrow E_i
Ei+n−k→Ei 的转移矩阵是
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′=Ei−k′′→Ei′,B′=Ei+n′−k′′→Ei′
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}
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
−
⌊
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+n′−k′==⇒B′=i+k−(n−⌊kn⌋∗k)i−(n−k)+⌊kn⌋∗(k−1)A−⌊kn⌋B−1
这样就求出了子问题中的转移矩阵,而由于进行了压缩操作,所以
1
≤
i
≤
k
1\le i\le k
1≤i≤k 中每个
i
i
i 的贡献都发生了变化,我们设原本
1
≤
i
≤
k
1\le i\le k
1≤i≤k 的贡献系数是
S
0
S_0
S0 ,原本
k
+
1
≤
i
≤
n
k+1\le i\le n
k+1≤i≤n 的贡献系数是
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+S1∗A+S1∗A2+⋯+S1∗A⌊kn⌋
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+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
签到题,列出每一维的生成函数然后做分治
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;
}