题目大意:
给你一个长度为n的序列,要你求这个序列中的最长国王子序列长度,如果长度小于n/2,输出-1,否则输出长度。
国王序列定义:对于一个序列a1,a2,a3,.....,an。当且仅当有一个整数q(1<=q<p)使得,且 q*ai-1 = ai (mod p)。
题解:
观察一下时间复杂度,如果用类似LIS的做法,时间复杂度肯定不行。
但是考虑到这里长度是和n/2比较,因此通过这个长度的限制,我们发现如果长度大于等于n/2,
那么这个子序列的数在原序列的最小间隔小于等于2。
解法一:推结论
因此我们就可以直接先求出原序列相邻的数以及相隔一个数的公比,用unordered_map存就行了。
然后我们可以发现如果一个公比的出现次数大于某个定值,那么以这个公比形成的序列就可能满足条件。
经估计出现次数大于n/8(大概是这个值,具体证明参照ecFinal)的公比形成的序列是满足条件的。
我们对这些公比形成的序列求出长度,并维护最大值就行了。
代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #include<unordered_map> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 2e5+7; ll b[N],p; unordered_map<ll,int> mp; int n,T; ll quick_mod(ll a,ll b){ ll res=1; while(b){ if(b&1) res=(res*a)%p; b>>=1; a=(a*a)%p; } return res%p; } ll inv(ll x) {return quick_mod(x,p-2);} int calc(ll q){ unordered_map<ll,int> dp; int res=0; RP(i,n,1){ dp[b[i]]=max(dp[b[i]],dp[b[i]*q%p]+1); res=max(res,dp[b[i]]); } return res; } int main(){ T=read(); while(T--){ mp.clear(); scanf("%d%lld",&n,&p); rp(i,1,n) scanf("%lld",&b[i]); rp(i,2,n){ ll q=b[i]*inv(b[i-1])%p; mp[q]++; } rp(i,3,n){ ll q=b[i]*inv(b[i-2])%p; mp[q]++; } int ans=0; for(auto it:mp) if(it.second>=n/8) ans=max(ans,calc(it.first)); if(ans*2<n) printf("-1\n"); else printf("%d\n",ans); } return 0; } /* 10 10 1 2 1 2 4 2 4 5 3 5 8 4 8 10 5 1 3 1 3 6 2 6 7 3 7 9 4 9 10 5 */
解法二:随机化算法
我们可以在序列中随机取两个数,那么这两个数出现在序列中的可能性大于1/4( (1/2)*(1/2) = (1/4) ).
当取得次数为x时,出现在答案的可能性就变成了 1 - (3/4)^x。
(因为每次不出现的概率为(3/4),x次都不出现就是(3/4)^x,那么出现至少一次的概率就是1-(3/4)^x)。
因此我们可以尽可能多次的随机取两个数,即x尽可能地大,这样正确性的概率就越大。
当然这两个数也不能就随机地取,这样可能导致公比相差很大。
而根据我们开头推出来的规律,如果我们取的数是答案中相邻的数,那么它们的最小距离小于等于2。
这样我们就可以随机取两个间隔小于等于2的数,以这两个数为公比,求出序列长度,迭代多次维护最大序列长度就行了。
trick:注意不能直接使用rand()函数,因为这个函数的范围比较小(0-32767),不符合题意,需要用到一个C++11的随机数生成函数(范围在[-maxint,maxint]之间)。
代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #include<random> #include <chrono> #include<bits/stdc++.h> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 2e5+7; ll inv[N],p,b[N]; int n; ll quick_mod(ll a,ll b){ ll res=1; while(b){ if(b&1) res=(res*a)%p; b>>=1; a=(a*a)%p; } return res%p; } ll solve(ll x,ll y){ ll q=b[y]*inv[x]%p; ll ans=2; int pre=x; RP(i,x-1,1){ if(b[pre]*inv[i]%p==q){ ans++; pre=i; } } pre=y; rp(i,y+1,n){ if(b[i]*inv[pre]%p==q){ ans++; pre=i; } } return ans; } int main(){ int T=read(); // srand(time(0)); mt19937 mt_rand(time(0)); while(T--){ scanf("%d%lld",&n,&p); rp(i,1,n) scanf("%lld",&b[i]); rp(i,1,n) inv[i]=quick_mod(b[i],p-2)%p; ll ans=-1; rp(i,1,150){ int num=mt_rand()%(n-1)+1; rp(j,num+1,num+2) ans=max(ans,solve(num,min(j,n))); } if(ans>=(n+1)/2) printf("%lld\n",ans); else printf("-1\n"); } return 0; }
参考链接:
2019ECfinal H - King ——随机算法|推结论
猜你喜欢
转载自blog.csdn.net/qq_43472263/article/details/104920301
今日推荐
周排行