题目链接:Link with Level Editor II
题意:
给定 n n n 个世界,每个世界是一个图,每个图有 m m m 个点, l l l 条有向边,选出一段最长的连续的世界,使得满足:
初始在第一个图的 1 1 1 号点,每次操作可以在当前图选择一条和当前点相连的边走向另一个点或者呆在该点,每次操作后都会从该世界传送到下一个世界对应点,在最后一个世界时恰好到达点 m m m 的方案数不超过 k k k 种。
分析:
显然区间右端点往右后方案是一定不减的,因此找到第一个超过 k k k 的右端点就可以停止,容易发现可以用双指针来搞。
由于每个图的点数很小,求点之间的到达方案数是很容易想到用矩阵维护的,那么实际上对于每一个区间的check,就是求 [ l , r ] [l,r] [l,r] 区间的转移矩阵积,那么这个做法的复杂度是 O ( n m 3 l o g n ) O(nm^3logn) O(nm3logn) 的,略微有点爆,复杂度瓶颈在每次询问区间在线段树上需要矩阵乘,每次矩阵乘需要 O ( m 3 ) O(m^3) O(m3) 的复杂度,于是用一个广为人知的优化,因为最后你只关心从 1 1 1 号点到 m m m 号点的方案数,可以只用一个长度为 m m m 的向量矩阵不断右乘线段树上的矩阵就可以了,向量乘矩阵的复杂度变成 O ( m 2 ) O(m^2) O(m2) 了,可以通过此题。
Code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<bitset>
#include<set>
#define ll long long
#define lowbit(x) x&(-x)
#define mp make_pair
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int mod=998244353;
const int maxn=5e3+5;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=x*10+(c-'0');
c=getchar();
}
return x*f;
}
int n,m,k;
struct matrix{
ll a[22][22];
inline void clear()
{
rep(i,1,m) rep(j,1,m) a[i][j]=0;
}
inline matrix operator * (const matrix &x)
{
matrix res;res.clear();
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=1;k<=m;k++)
{
res.a[i][j]+=(a[i][k]*x.a[k][j]);
}
}
}
return res;
}
};
struct line{
ll a[22];
inline void init()
{
rep(i,1,m) a[i]=0;
}
};
inline line mul(line x,matrix y)
{
line res;res.init();
rep(i,1,m)
{
rep(j,1,m)
{
res.a[i]+=x.a[j]*y.a[j][i];
}
}
return res;
}
line tmp;
struct SegTree{
struct node{
int l,r;
matrix pre;
}t[maxn<<2];
inline void build(int p,int l,int r)
{
t[p].l=l;t[p].r=r;
if(l==r)
{
int tot=read();
t[p].pre.clear();
rep(i,1,m) t[p].pre.a[i][i]=1;
rep(i,1,tot)
{
int x=read(),y=read();
t[p].pre.a[x][y]++;
}
return;
}
int mid=(t[p].l+t[p].r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].pre=t[p*2].pre*t[p*2+1].pre;
}
inline void ask(int p,int l,int r)
{
if(t[p].l>=l&&t[p].r<=r)
{
tmp=mul(tmp,t[p].pre);
return;
}
int mid=(t[p].l+t[p].r)/2;
if(l<=mid) ask(p*2,l,r);
if(r>mid) ask(p*2+1,l,r);
}
}S;
int T;
inline bool check(int l,int r)
{
tmp.init();tmp.a[1]=1;
S.ask(1,l,r);
return tmp.a[m]<=k;
}
signed main()
{
T=read();
while(T--)
{
int ans=0;
n=read(),m=read(),k=read();
S.build(1,1,n);
for(int i=1,j=1;i<=n;i++)
{
if(n-i+1<=ans) break;
while(j<n&&check(i,j+1)) j++;
ans=max(ans,j-i+1);
}
printf("%d\n",ans);
}
}