很套路的矩阵乘法,但是考试的时候没有想到…
定义 表示前 行 个格子不选.
- 斐波那契数列
- 第 列选择有两种方案
- 第 列选择的话就只有一种方案且与前一个放置位置的列的差至少为2.
我们可以精简一下式子:
令 ,则
这里利用了一个性质: .(用数学归纳法可以证明)
所以我们定义一个5维向量 ,用矩阵乘法优化一下即可.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar()
using namespace std;
typedef long long ll;
const int N=5,mod=1e9+7;
const ll inf=1e18;
template<class o> void qr(o &x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)){if(c=='-') f=-1; c=gc;}
while(isdigit(c))x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x<0) putchar('-'),x=-x;
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr1(o x) {qw(x); putchar(' ');}
template<class o> void pr2(o x) {qw(x); puts("");}
int T;
ll ans,n;
struct rec {
ll a[N][N];
rec() {memset(a,0,sizeof(a));}
rec operator *(rec b) const {
rec c;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++) if(a[i][j])
for(int k=0;k<N;k++)
c.a[i][k]+=a[i][j]*b.a[j][k]%mod;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++) c.a[i][j]%=mod;
return c;
}
} f,g;
int main() {
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
qr(T); while(T--) {
qr(n); f=rec(); g=rec();
f.a[0][2]=1; f.a[0][4]=mod-2;
g.a[0][0]=1; g.a[0][1]=1;
g.a[1][0]=1;
g.a[2][0]=2; g.a[2][2]=1; g.a[2][3]=1;
g.a[3][2]=1;
g.a[4][0]=g.a[4][4]=1;
while(n) {
if(n&1) f=f*g;
n /= 2; g=g*g;
}
pr2(f.a[0][0]);
}
return 0;
}
正反向跑最短路.
枚举每条边 ,然后如果 的来源( )不同,那么就合法.
为啥一定会遍历到最优解:
的路径上,
有一部分点在正向图中由 得到,有一部分点在反向图中由 得到,
而且显然至少存在一条边沟通这两个点集.
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar()
#define pi pair<ll,int>
#define mk make_pair
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+10,M=1e6+10;
const ll inf=1e18;
template<class o> void qr(o &x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)){if(c=='-') f=-1; c=gc;}
while(isdigit(c))x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x<0) putchar('-'),x=-x;
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr1(o x) {qw(x); putchar(' ');}
template<class o> void pr2(o x) {qw(x); puts("");}
int T,n,m,t,b[N],st[2][N];
ll d[2][N],ans;
struct edge{int y,next; ll d;}a[M]; int len,last[N];
struct rec{int x,y,z;} tmp[M];
void ins(int x,int y,ll d) {a[++len]=(edge){y,last[x],d}; last[x]=len; }
void dijkstra(int f) {
priority_queue<pi> q;
memset(d[f]+1,63,sizeof(ll[n]));
for(int i=1;i<=t;i++) d[f][b[i]]=0,st[f][b[i]]=b[i],q.push(mk(0,b[i]));
memset(last+1,0,sizeof(int[n])); len=0;
if(f) for(int i=1;i<=m;i++) ins(tmp[i].y,tmp[i].x,tmp[i].z);
else for(int i=1;i<=m;i++) ins(tmp[i].x,tmp[i].y,tmp[i].z);
while(!q.empty()) {
int x=q.top().second;
ll D=-q.top().first; q.pop();
if(d[f][x]^D) continue;
for(int k=last[x],y;k;k=a[k].next) {
y=a[k].y;
if(d[f][x]+a[k].d<d[f][y]) {
d[f][y]=d[f][x]+a[k].d;
q.push(mk(-d[f][y],y));
st[f][y]=st[f][x];
}
}
}
}
int main() {
qr(T); while(T--) {
qr(n); qr(m); qr(t);
for(int i=1;i<=m;i++) qr(tmp[i].x),qr(tmp[i].y),qr(tmp[i].z);
for(int i=1;i<=t;i++) qr(b[i]);
ans=inf;
dijkstra(0); dijkstra(1);//0正向,1反向
for(int i=1;i<=m;i++)
if(st[0][tmp[i].x]^st[1][tmp[i].y])
ans=min(ans,d[0][tmp[i].x]+d[1][tmp[i].y]+tmp[i].z);
pr2(ans);
}
return 0;
}
考试的时候只想出了 的情况,其实再仔细想想即可得到正解.
树剖中线段树维护差分权值*被覆盖次数之和即可.
#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar()
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=5e4+10,M=N,mod=998244353;
const ll inf=1e18;
template<class o> void qr(o &x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)){if(c=='-') f=-1; c=gc;}
while(isdigit(c))x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x<0) putchar('-'),x=-x;
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr1(o x) {qw(x); putchar(' ');}
template<class o> void pr2(o x) {qw(x); puts("");}
int n,m,t,fa[N],p[N],dep[N],sz[N],son[N],mx,f[N],d[N];
struct edge{int y,next;}a[N]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len;}
ll power(ll a,ll b=t) {
ll c=1;
while(b) {
if(b&1) c=c*a%mod;
b /= 2; a=a*a%mod;
}
return c;
}
template<class o>void upd(o &x) {x+=x>>31&mod; if(x>=mod) x-=mod; }
struct rec {
int x,y,id;
bool operator <(rec b) const {
return x<b.x;
}
} q[M]; int ans[M];
int sta[N],top;
void dfs(int x) {
son[x]=0; sz[x]=1;
for(int k=last[x],y;k;k=a[k].next) {
y=a[k].y; dep[y]=dep[x]+1; dfs(y);
if(sz[son[x]]<sz[y]) son[x]=y;
sz[x]+=sz[y];
}
}
//树剖
int id[N],z,b[N],tp[N];
void DFS(int x,int T) {
tp[x]=T; id[x]=++z; upd(b[z]=b[z-1]+d[dep[x]]);
if(son[x]) DFS(son[x],T);
for(int k=last[x],y;k;k=a[k].next)
if((y=a[k].y)^son[x]) DFS(y,y);
}
int s[N<<2];//权*次数之和
int ad[N<<2];//懒标记
void upd(int x,int l,int r,int c) {upd(s[x]+=(ll)(b[r]-b[l-1]+mod)*c%mod);}
void add(int x,int l,int r,int L,int R) {
if(L<=l&&r<=R) {upd(x,l,r,1); ad[x]++; return ;}
int mid=(l+r)>>1;
if(ad[x]) {
upd(lc,l,mid,ad[x]);
ad[lc]+=ad[x];
upd(rc,mid+1,r,ad[x]);
ad[rc]+=ad[x];
ad[x]=0;
}
if(L<=mid) add(lc,l,mid,L,R);
if(mid< R) add(rc,mid+1,r,L,R);
upd(s[x]=s[lc]+s[rc]);
}
int ask(int x,int l,int r,int L,int R) {
if(L<=l&&r<=R) return s[x];
int mid=(l+r)>>1;
if(ad[x]) {
upd(lc,l,mid,ad[x]);
ad[lc]+=ad[x];
upd(rc,mid+1,r,ad[x]);
ad[rc]+=ad[x];
ad[x]=0;
}
return ((L<=mid?ask(lc,l,mid,L,R):0LL)+
(mid<R?ask(rc,mid+1,r,L,R):0LL))%mod;
}
void add(int x) {
while(x) {
int y=tp[x];
add(1,1,n,id[y],id[x]);
x=fa[y];
}
}
int solve(int x) {
int y; ll s=0;
while(x) {
y=tp[x];
s+=ask(1,1,n,id[y],id[x]);
x=fa[y];
}
return s%mod;
}
int main() {
// freopen("c.in","r",stdin);
// freopen("c.out","w",stdout);
qr(n); qr(m); qr(t);
for(int i=1;i<=n;i++) p[i]=power(i),upd(d[i]=p[i]-p[i-1]);
for(int i=2;i<=n;i++) qr(fa[i]),ins(fa[i],i);
for(int i=1;i<=m;i++) qr(q[i].x),qr(q[i].y),q[i].id=i;
sort(q+1,q+m+1); dep[1]=1; dfs(1); DFS(1,1);
for(int i=1,p=1;i<=n;i++) {
add(i);
while(q[p].x==i)
ans[q[p].id]=solve(q[p].y),p++;
}
for(int i=1;i<=m;i++) pr2(ans[i]);
return 0;
}