Newcoder 128 C.寻宝(树形DP)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V5ZSQ/article/details/82949062

Description

由依是战线佯攻部队的辅助人员,在岩泽消失之后,企图代替岩泽成为 G D M GDM 主唱。但是 S S S SSS 战线的领袖仲村由理是不会轻易让她加入的,于是由理子给了由依一项艰巨的任务:去一个地下迷宫寻找宝石!

这个迷宫由 n n 个房间组成,编号为 0 0 n 1 n-1 ,每个房间里都有一颗宝石,房间通过单向通道连接。每个房间里有两个门:一个通向第 R R 个房间 ( R = ( a v 2 + b v + c )   m o d   n ) (R=(a·v^2+ b·v + c)\ mod\ n) ,另一个通向迷宫出口,一旦离开迷宫,便会触发自毁机关,将再也没有机会继续收集宝石。现在,她可以在任何地点进入迷宫,沿隧道移动并收集宝石。

由依想尽可能地收集宝石,你能算出她能从迷宫中获得的最大宝石数量是多少吗?

Input

输入的第一行包含一个整数 T T ,表示测试组数。
每个测试用例前面都有一个空白行。
每个测试用例包含四个整数 a b c a,b,c n n

( T 10 , n 2 24 ) (T\le 10,n\le 2^{24})

Output

对于每个测试用例输出一个数表示她可以从迷宫中获取的最大宝石数量。

Sample Input

3

1 2 0 64

0 2 1 47

0 3 5 128

Sample Output

5
23
64

Solution

每个点出度为 1 1 ,故每个连通分支都是基环,首先找到环上所有点,从这些点出发的答案即为环上点的个数,之后对基环每点为根的树直接往下转移即可,时间复杂度 O ( n ) O(n)

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
#define maxn (1<<24)+5
int T,n,vis[maxn],dep[maxn],dp[maxn],sta[maxn],Next[maxn],p,a,b,c;
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d",&a,&b,&c,&n);
		for(int i=0;i<n;i++)
		{
			vis[i]=0;
			Next[i]=((ll)a*i%n*i%n+(ll)b*i%n+c)%n;
		}
		for(int i=0;i<n;i++)
			if(!vis[i])
			{
				int j=i,res=0,p=0;
				while(!vis[j])
				{
					sta[p++]=j;
					dep[j]=res++;
					vis[j]=1;
					j=Next[j];
				}
				res-=dep[j];
				if(vis[j]!=2)
				{
					while(1)
					{
						p--;
						vis[sta[p]]=2;
						dp[sta[p]]=res;
						if(sta[p]==j)break;
					}
				}
				while(p)
				{
					p--;
					vis[sta[p]]=2;
					dp[sta[p]]=dp[Next[sta[p]]]+1;
				}
			}
		int ans=0;
		for(int i=0;i<n;i++)ans=max(ans,dp[i]);
		printf("%d\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/V5ZSQ/article/details/82949062
128