2019ECfinal
https://vjudge.net/contest/406509#overview
H - King
题目大意
给定长度为(n)的序列(A)。
问(A)的最长子序列(B),满足以下条件,最长为多少?
[B=(b_1,b_2,..,b_m)
]
[forall i in [2,m],q b_{i-1}equiv b_i pmod p.
]
如果最大长度小于(frac{n}{2}),则直接输出(-1)
题目解法
关键限制条件为子序列只有长度够(frac{n}{2})才算满足要求
因此其中出现在(B)中概率最大的为(A)中邻近的两个位置((a_i,a_{i+1}))或者((a_i,a_{i+2}))。
用(unordered\_map)对于这些点对求出(q)进行计数。
如果要满足答案,对应(q)的数量至少为
[lfloor frac{n+3}{4}
floor -1
]
对于所有满足条件的(q)进行验证,最多验证(8)次。
时间复杂度(O(nlog(n)))
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int a[N], mod;
unordered_map<int, int> qNum, qLen;
int myPow(int p, int q)
{
int re = 1;
for (; q; q >>= 1)
{
if (q & 1)
re = (ll)re * p % mod;
p = (ll)p * p % mod;
}
return re;
}
int calcQ(int a1, int a2)
{
return (ll)a2 * myPow(a1, mod - 2) % mod;
}
int main()
{
freopen("h.in", "r", stdin);
int T;
cin >> T;
while (T--)
{
qNum.clear();
int n, minLen, ans = 0;
cin >> n >> mod;
minLen = (n + 1) / 2;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1; i < n; ++i)
{
int q = calcQ(a[i], a[i + 1]);
qNum[q]++;
//cout << q<< ' ';
}
for (int i = 1; i + 2 <= n; ++i)
{
int q = calcQ(a[i], a[i + 2]);
qNum[q]++;
//cout << q<< ' ';
}
//cout << endl;
for (auto i : qNum)
{
//cout << '(' << i.first << ',' << i.second << endl;
if (i.second + 1 < (n + 3) / 4)
continue;
qLen.clear();
int q = i.first;
for (int j = 1; j <= n; ++j)
{
int baseLen = 0;
if (qLen.find(a[j]) != qLen.end())
{
baseLen = qLen[a[j]];
}
qLen[(ll)a[j] * q % mod] = baseLen + 1;
ans = max(ans, baseLen + 1);
}
}
if (ans >= minLen)
cout << ans << endl;
else
cout << -1 << endl;
}
return 0;
}