Codeforces Round #797 (Div. 3)
比赛链接
Codeforces Round #797 (Div. 3)
E.Price Maximization
本题有多组测试数据。
我们有 \(n\) 个礼物,而最终我们需要将所有的礼物两两包成 \(\frac{n}{2}\) 个包裏。
每一个礼物 \(i\) 都有其价值 \(a_{i}\) ,而含有礼物 \(i\) 与礼物 \(j\) 的包裏的价值是 \(\left\lfloor\frac{a_{i}+a_{j}}{k}\right\rfloor\) 。
我们需要找出一个方案来使得所有包裏的价值之和最大,并求出这个最大值。
输入
6
6 3
3 2 7 1 4 8
4 3
2 1 5 6
4 12
0 0 0 0
2 1
1 1
6 10
2 0 0 5 9 4
6 5
5 3 8 6 3 2
输出
8
4
0
2
1
5
解题思路
双指针
结论:\(\left \lfloor \frac{a+b}{k} \right \rfloor=\left \lfloor \frac{a}{k} \right \rfloor +\left \lfloor \frac{b}{k} \right \rfloor + (a\%k+b\%k>k)\)
证明:\(\left \lfloor \frac{a+b}{k} \right \rfloor=\left \lfloor \frac{a/k*k+a\%k+b/k*k+b\%k}{k} \right \rfloor\),其中 \(a\%k\) 和 \(b\%k\) 都要小于 \(k\),\(a/k*k+b/k*k\) 整除 \(k\),另外其他部分取决于 \(a\%k+b\%k\) 是否大于 \(k\)
利用上述结论,先求出 \(a_i\%k\) 的整除部分之和,再求出 \(a_i\) 对 \(k\) 的余数,排序,现在问题转化为在序列 \(a\) 中找出最多的对数使得 \(a_i+a_j>k\),其中更大的数一定要选,则可以采用双指针,固定更大的数,移动更小的数的指针
- 时间复杂度:\(O(nlogn)\)
代码
// Problem: E. Price Maximization
// Contest: Codeforces - Codeforces Round #797 (Div. 3)
// URL: https://codeforces.com/contest/1690/problem/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
// #define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int t,n,k;
int main()
{
for(cin>>t;t;t--)
{
cin>>n>>k;
LL s=0;
vector<int>a(n);
for(int i=0;i<n;i++)cin>>a[i],s+=a[i]/k,a[i]%=k;
sort(a.begin(),a.end());
for(int i=n-1,j=0;i>j;i--,j++)
{
while(i>j&&a[i]+a[j]<k)j++;
if(i>j&&a[i]+a[j]>=k)s++;
}
cout<<s<<'\n';
}
return 0;
}
F. Shifting String
给定一长为 \(n\) 的字符串 \(s\)(下标从 \(1\) 开始) 与 \(1\sim n\) 的排列 \(p\)。定义 \(s^0=s,s^k_i=s^{k-1}_{p_i}\)。求最小的 \(k>0\) 使 \(s^{k}=s\) 。 \(t\) 组数据。
- \(t \leq 5000, n \leq 200\) 。
解题思路
置换群,KMP求最小循环节
先按置换群求出每个环形成的字符串,由于每个字符串可能会出现循环节,求出每个环的循环节长度的最小公倍数即为答案,所以本题关键在于求最小循环节,有如下 KMP 定理求解:
假设 \(s\) 长度为 \(n\),如果存在循环节,则其长度为 \(L=n-ne[n]\),另外有如下两条性质:
- 如果 \(n\) 可以被 \(n-ne[n]\) 整除,则 \(s\) 存在循环节,周期 \(T=n/L\)
- 否则,还需添加字母补全,需要补充的字母个数为 \(L-n\%L=L-ne[n]\%L\),其中 \(L=n-ne[n]\)
- 时间复杂度:\(O(nlogn)\)
代码
// Problem: F. Shifting String
// Contest: Codeforces - Codeforces Round #797 (Div. 3)
// URL: https://codeforces.com/contest/1690/problem/F
// Memory Limit: 256 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=205;
int p[N],t,n;
signed main()
{
for(cin>>t;t;t--)
{
string s;
cin>>n;
cin>>s;
s=' '+s;
for(int i=1;i<=n;i++)cin>>p[i];
vector<bool> vis(n+1);
int res=1;
for(int i=1;i<=n;i++)
{
if(vis[i])continue;
string t;
for(int j=i;!vis[j];j=p[j])t+=s[j],vis[j]=true;
int m=t.size();
t=' '+t;
vector<int> ne(m+1);
for(int i=2,j=0;i<=m;i++)
{
while(j&&t[i]!=t[j+1])j=ne[j];
if(t[i]==t[j+1])j++;
ne[i]=j;
}
if(m%(m-ne[m])==0)res=1ll*res*(m-ne[m])/__gcd(res,m-ne[m]);
else
res=1ll*res*m/__gcd(res,m);
}
cout<<res<<'\n';
}
return 0;
}