最长不下降子序列

  可以发现,在取模意义下二次函数的值具有周期性。
  证明:

  在modD意义下,二次函数的值不会超过D,那么,当序列长度超过D时,根据抽屉原理,一定会有某个数出现了两次,由此出现循环节。
  证毕

  那么可以考虑寻找前面那一段不是完整的循环节的序列加上由\(t\)段循环节构成的序列的序列。
  从那里面找出一段最长不下降子序列,然后,从中间的循环节中选循环节数个刚才求出的序列的末尾数字。然后在从最后那段不满一个循环节的序列中求一个以刚刚求的序列的结尾数字为开始的最长上升子序列。

代码
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
	#define rr register 
	typedef long long ll;
	const int inf=INT_MAX;
	const ll N=300;
	ll n;
	int t,l,T,A,B,C,D;
	int en,st;
	ll ans;
	int s[N*N],a[N];
	int pos[N],f[N];
	bool co[N];
	template<typename T>
	T cmax(rr T x,rr T y){return x>y?x:y;}	
	vector<int> reco;
	ll read()
	{
		rr ll x_read=0,y_read=1;
		rr char c_read=getchar();
		while(c_read<'0'||c_read>'9')
		{
			if(c_read=='-') y_read=-1;
			c_read=getchar();
		}
		while(c_read<='9'&&c_read>='0')
		{
			x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
			c_read=getchar();
		}
		return x_read*y_read;
	}
	#define lc id<<1
	#define rc id<<1|1
	class Line_tree
	{
		private:
			int t[N<<4];
		public:
			void insert(int,int,int,int,int);
			void clear();
			int query(int,int,int,int,int);
	}u;
	void Line_tree::clear(){memset(t,0,sizeof t);}
	void Line_tree::insert(int id,int l,int r,int pos,int val)
	{
		if(l==r){t[id]=cmax(t[id],val);return;}
		int mid=(l+r)>>1;
		if(pos<=mid)  insert(lc,l,mid,pos,val);
		else insert(rc,mid+1,r,pos,val);
		t[id]=cmax(t[lc],t[rc]);
	}
	int Line_tree::query(int id,int l,int r,int st,int en)
	{
		if(st<=l&&r<=en) return t[id];
		int mid=(l+r)>>1;
		int ret=-inf;
		if(st<=mid) ret=cmax(ret,query(lc,l,mid,st,en));
		if(mid<en) ret=cmax(ret,query(rc,mid+1,r,st,en));
		return ret;
	}
	#undef lc
	#undef rc
};
using namespace STD;
int main()
{
	n=read();
	T=read(),A=read(),B=read(),C=read(),D=read();
	s[1]=T;
	for(rr int i=2;i<=N*N-5;i++)
		s[i]=(A*s[i-1]*s[i-1]+B*s[i-1]+C)%D;
	for(rr int i=1;i<=min(N*N-5,n);i++)
		if(!pos[s[i]]) pos[s[i]]=i;
		else
		{
			l=pos[s[i]]-1;
			t=i-pos[s[i]];
			st=i;
			break;
		}
	ll stop=0;
	for(rr int i=l+1;i<=min(N*N-5,n);i++)
		if(i-l==max(t*t,l))
		{
			stop=i;
			break;
		}
	//cout<<"STOP: "<<stop<<'\n';
	if(!stop) stop=n;
	for(rr int i=1;i<=stop;i++)
	{
		ll x=u.query(1,0,D,0,s[i]);
		x++;
		u.insert(1,0,D,s[i],x);	
		if(x>=ans)
		{
			if(ans==x) reco.push_back(i);
			else
			{
				ans=x;
				reco.clear();
				reco.push_back(i);
			}
		}
	}
	if(t&&(n-stop)>0)
	{
		n-=stop;
		ll n_=n;
		n-=(n%t);
		ans+=(n/t);
		//必须跑以s[en]为开头的lis。
		a[1]=s[st];
		for(rr int i=2;i<=n_%t;i++)
			a[i]=(A*a[i-1]*a[i-1]+B*a[i-1]+C)%D;
		int add=0;
		for(rr int i=0;i<reco.size();i++)
		{
			int j=1;
			memset(co,0,sizeof co);
			memset(f,0,sizeof f);
			while(a[j]!=s[reco[i]]&&j<=n_%t) j++;
			st=j;
			while(j<=n_%t)
			{
				f[j]=1;
				for(rr int k=st;k<j;k++)
					if(a[k]<=a[j]&&co[k])
					{
						co[j]=1;
						f[j]=max(f[j],f[k]+1);
					}
				add=max(f[j],add);
				j++;
			}
		}
		ans+=add;
	}
	cout<<ans<<'\n';
}
posted @ 2021-08-03 18:37  Geek_kay  阅读(68)  评论(0编辑  收藏  举报