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;
}
posted @ 2022-06-24 09:48  zyy2001  阅读(54)  评论(0编辑  收藏  举报