[HDU多校 第四场] Link with Level Editor II

6 篇文章 0 订阅
1 篇文章 0 订阅
本文介绍了一道编程题目,涉及图论、区间查询和矩阵快速幂优化。题目要求在给定的有向图序列中,找出一段连续的世界,从第一个点开始,通过不超过k种方案达到最后一个点。利用双指针和矩阵快速幂优化,将复杂度降低到O(nm^2logn),从而解决了爆栈的问题。
摘要由CSDN通过智能技术生成

题目链接: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);
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值