snoi多校模擬賽1.15 t1 travel



解法:樹形dp,dp[k][j][i]表示模k意義下以i爲終點長度爲i的路徑是否存在。

轉移:因爲每次最多走一條邊,所以只需要枚舉k轉移即可,方程見代碼,很容易想到。邊界條件就是能直接到的點的dp距離值=1。代碼中爲了簡化,使用了三個數組,循環使用,來表示當前點狀態,後續狀態,總狀態。這樣就能使寫代碼快很多,還有就是注意,後續狀態轉移完之後,還要再枚舉一次,得到完整的後續狀態,不然會WA!

ps:從標程的寫法上漲姿勢了,終於懂得如何在一堆數組中簡化自己的代碼了!

代碼:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define register int int
using namespace std;
vector<pair<int,int> >e[3005];
int n,q,sta[3005],cost[3005],uu,vv,ww,u[100005],k[100005],maxk;
bool useing[3005],nex[3005][105],dp[105][3005][105];
inline int read()
{
	int res=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		res=res*10+c-'0';
		c=getchar();
	}
	return res*f;
}
void init(int now,int fa,int k,bool f[3005][105])
{
	f[now][0]=1;
	for(int i=0,len=e[now].size();i<len;i++)
	{
		int to=e[now][i].first,pay=e[now][i].second;
		if(to==fa) continue;
		init(to,now,k,f);
		for(int j=0;j<k;j++)
			if(f[to][j])
				f[now][(j+pay)%k]=1;
	}
}
void DP(int now,int fa,int k,bool f[3005][105])
{
	int head=0;
	for(int i=0,len=e[now].size();i<len;i++)
	{
		int to=e[now][i].first,pay=e[now][i].second;//把除了父親之外的出邊保存到一個棧裏,之後更新
		head++;
		sta[head]=to;
		cost[head]=pay;
	}
	for(int i=0;i<k;i++)
	{
		useing[i]=nex[now][i];//useing用來更新nex,nex用來更新dp,這一輪的nex就是下一輪的dp,所以再次更新,循環往復
		f[now][i]|=nex[now][i];//useing是當前狀態,nex是後續狀態,dp是整個狀態,所以分別是1,2,3維
	}
	useing[0]|=1;
	for(int i=1;i<=head;i++)
	{
		int v=sta[i],pay=cost[i];
		for(int j=0;j<k;++j) 
			nex[v][(j+pay)%k]|=useing[j];//模k意義下能到達的邊,更新
		for(int j=0;j<k;++j) 
			useing[(j+pay)%k]|=f[v][j];
	}
	for(int i=0;i<k;i++)
		useing[i]=nex[now][i];
	useing[0]|=1;
	for(int i=head;i>=1;i--)//剛纔是從前往後推,現在nex發生了變化,所以再次從後向前推一遍,以至於可以轉移所有狀態
	{
		int v=sta[i],pay=cost[i];
		for(int j=0;j<k;++j) 
			nex[v][(j+pay)%k]|=useing[j];
		for(int j=0;j<k;++j) 
			useing[(j+pay)%k]|=f[v][j];
	}
	for(int i=0,len=e[now].size();i<len;i++)//遍歷出邊
	{
		int to=e[now][i].first;
		if(to!=fa) DP(to,now,k,f);
	}
}
void solve(int k,bool f[3005][105])
{
	init(1,0,k,f);
	memset(nex,0,sizeof(nex));
	DP(1,0,k,f);
}
int main()
{
	freopen("mtree.in","r",stdin); freopen("mtree.out","w",stdout);
	n=read();
	for(int i=1;i<n;i++)
	{
		uu=read();vv=read();ww=read();
		e[uu].push_back(make_pair(vv,ww));
		e[vv].push_back(make_pair(uu,ww));
	}
	q=read();
	for(int i=1;i<=q;i++)
	{
		u[i]=read();k[i]=read();
		maxk=max(maxk,k[i]);
	}
	for(int i=1;i<=maxk;i++)
		solve(i,dp[i]);
	for(int i=1;i<=q;i++)
		for(int j=k[i]-1;j>=0;j--)
			if(dp[k[i]][u[i]][j])
			{
				printf("%d\n",j);
				break;	
			}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章