Newcoder 128 C.寻宝(树形DP)

Description

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

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

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

Input

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

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

Output

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

Sample Input

3

1 2 0 64

0 2 1 47

0 3 5 128

Sample Output

5
23
64

Solution

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值