cf1832
cf1832
A. New Palindrome
看回文字符串经过重排是不是还能组成回文字符串。如果字符串不计只出现一次的字符,其余有多个字符都出现偶数次,那么就重排可以组成新的回文字符串。
// Problem: A. New Palindrome
// Contest: Codeforces - Educational Codeforces Round 148 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1832/problem/A
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
#include<vector>
#include<map>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
typedef long long LL;
string s;
int cnt[26];
int res;
int T;
int main(){
cin>>T;
while(T--){
cin>>s;
memset(cnt,0,sizeof(cnt));
res=0;
for(int i=0,n=s.length();i<n;i++){
cnt[s[i]-'a']++;
}
for(int i=0;i<26;i++){
if(cnt[i]>=2) res++;
}
if(res>1) puts("YES");
else puts("NO");
}
return 0;
}
B. Maximum Sum
题目链接
每次在k次操作中,每次操作删掉最小的两个数或者最大的一个数。求最后剩下的数的和最大的情况。
直觉上贪心是最小两个数和与最大一个数谁小删除谁,但是这样链样例都过不去。因为每次删除的贡献并不相等。这样的情况遍历才能考虑周全
排完序用前缀和遍历一下就可以考虑所有情况了。
核心公式:res=max(res,sum[n-(k-i)])-sum[i<<1]
n-(k-j)是删除掉几个最大数的前缀和,此时减去i<<1是删除掉几个最小数的前缀和。剩下的就是这种删除策略执行后的值。
// Problem: B. Maximum Sum
// Contest: Codeforces - Educational Codeforces Round 148 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1832/problem/B
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
#include<vector>
#include<map>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
typedef long long LL;
typedef pair<LL,int> PLI;
const int N=2e5+10;
int T;
int n,k;
LL a[N];
LL sum[N];
void output(){
for(int i=1;i<=n;i++){
cout<<sum[i]<<" ";
}
cout<<endl;
}
int main(){
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
}
// output();
LL res=0;
for(int i=0;i<=k;i++)
{
res=max(res,sum[n-(k-i)]-sum[i<<1]);
}
printf("%lld\n",res);
}
return 0;
}
C. Contrast Value
题目链接
给定一个数组定义一个条件:\(\vert a_1-a_2\vert+\vert a_2-a_3\vert+\cdots +\vert a_{n-1} -a_n\vert\).
要求给出一个非空子数组其条件\(\vert b_1-b_2\vert+\vert b_2-b_3\vert+\cdots +\vert b_{n-1} -b_n\vert\)不变.
求符合条件的子数组最小长度
考虑条件若去掉绝对值符号,则上述求和公式变为\(b_1-b_n\),但是去掉绝对值的条件是相邻的绝对值内符号相等,那么把所有相邻的绝对值符号内式子符号相等的情况合并即可,合并后剩下式子加1即为最小规模
// Problem: C. Contrast Value
// Contest: Codeforces - Educational Codeforces Round 148 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1832/problem/C
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
#include<vector>
#include<map>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
typedef long long LL;
const int N=3e5+10;
int T,n;
int a[N];
void output(vector<int> q){
for(int i=0,si=q.size();i<si;i++){
cout<<q[i]<<" ";
}
cout<<endl;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
vector<int> q;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
q.push_back(a[0]);
for(int i=1;i<n;i++){
if(q[q.size()-1]!=a[i]) q.push_back(a[i]);
}
// output(q);
int res=1;
if(q.size()<=1){
printf("%d\n",res);
}else{
int fla= q[0]-q[1]>0?1:0;
for(int i=1,siz=q.size();i<siz-1;i++){
int t=q[i]-q[i+1]>0?1:0;
if(t!=fla){
fla=t;
res++;
}
}
printf("%d\n",res+1);
}
}
return 0;
}
D1. Red-Blue Operations (Easy Version)
给定一个长度为n的序列初始时均为红色。
给定k个操作中的第i个来说:
- 对于红色的数那么就变为蓝色,同时该数加上i
- 对于蓝色的数那么就变为红色,同时该数减去i
求最小数最大时最小数的大小
可以证明,要保证最优,必须最小的加最大可加的数,依次类推。
对于操作数k,有以下几种情况:
- k<=n,那么每个数都可以增加,按照最优策略加对应的数取最小即可。
- k>n,必定存在某些数发生减法操作,但是可以发现最后减法操作就是将若干个
-1
均分到数列中,那么最后的策略就还是先考虑数先增加再考虑提取数减小。 - 考虑当数列第一次全为蓝色时,若还剩余操作数m,考虑减法操作。此时先求出所有的数大于最小数的diff,若大于\(\lfloor m \rfloor\) 则最小数不变,但是当小于时最小数此时减去\(\lceil k/n \rceil\)即可。
// Problem: D1. Red-Blue Operations (Easy Version)
// Contest: Codeforces - Educational Codeforces Round 148 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1832/problem/D1
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N=1e3+10;
int n,q;
int a[N],backup[N];
void output()
{
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
int main()
{
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>backup[i];
}
sort(backup+1,backup+1+n);
while(q--)
{
int k;
cin>>k;
for(int i=1;i<=n;i++)
{
a[i]=backup[i];
}
for(int i=1;i<=n;i++)
{
if(!k) break;
if(k%2==0&&i==n) break;
a[i]+=k--;
}
// output();
k/=2;
sort(a+1,a+1+n);
LL diff=0;
for(int i=1;i<=n;i++)
{
diff+=a[i]-a[1];
}
int res=a[1];
if(k>diff)
{
k-=diff;
res-=(k+n-1)/n;
}
cout<<res<<' ';
}
return 0;
}
D2. Red-Blue Operations (Hard Version)
和D1的唯一区别是操作数和数列长度增大,此时上述未优化代码会超时。
此时考虑每个数都会加上\(k-i\) i为该数的位置,而且只需要知道最小的即可,那么可以先预处理数组pre[i]=min(pre[i-1],a[i]-i)。
对于可以大于最小数的总数可以维护所有数的总和来求得。总的来说逻辑不变。
具体优化见代码。
// Problem: D2. Red-Blue Operations (Hard Version)
// Contest: Codeforces - Educational Codeforces Round 148 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1832/problem/D2
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N=2e5+10;
int n,q,k;
LL s,ps;
LL a[N],pre[N];
int main()
{
cin>>n>>q;
s-=(n-1)*(n-2)/2;
for(int i=0;i<n;i++)
{
cin>>a[i];
s+=a[i];
}
sort(a,a+n);
pre[0]=a[0];
for(int i=1;i<n;i++)
{
pre[i]=min(pre[i-1],a[i]-i);//预处理前缀,pre[i]是第i位前最小的a[i]-i
}
ps=s;//存总和备份防止篡改
while(q--)
{
s=ps;
LL ans=0x3f3f3f3f;
cin>>k;
if(k<n)
{
ans=min(pre[k-1]+k,a[k]);//当数组长度大于操作数时,前面发生加操作的最小数,和未发生加操作的最小数取min即可
}else if(n==1)//n==1特判
{
ans=a[0]-k/2;
if(k%2)
{
ans+=k;
}
}else
{
//此时没有第一时间对最后一个数操作
s+=(LL)k*(n-1);//总和加上(n-1)个k
ans=pre[n-2]+k;//此时以发生加操作的数一定是pre[n-2]+k
k-=n-1;
if(k%2)//剩余操作数奇数,那么最后一个数会发生加操作
{
s+=k;
ans=min(ans,a[n-1]+k);
k--;
}else//偶数则将多个-1分摊
{
ans=min(ans,a[n-1]);
}
s-=ans*n;//可以在保持最小值不变情况下的可以减的数
k/=2;
if(k>s)
{
ans-=(k-s-1)/n+1;
}
}
cout<<ans<<" ";
}
return 0;
}
E. Combinatorics Problem
注意k<=5这个条件,可以找规律找出解法。
以样例为例,n=5,\(a_1=8\),x=2,y=3,m=100,k=2
\(b_1=0\)
\(b_2=a_1\) \(d_2=a_1\) \(e_2=a_1\)
\(b_3=3a_1+a_2\) \(d_3=2a_1+a_2\) \(e_3=a_1+a_2\)
\(b_4=6a_1+3a_2+a_3\) \(d_4=3a_1+2a_2+a_3\) \(e_4=2a_1+a_2+a_3\)
\(b_5=10a_1+6a_2+3a_3+a_4\) \(d_5=b_5-b_4=4a_1+3a_2+2a_3+a_4\) \(e_5=d_5-d_4=a_1+a_2+a_3+a_4\)
多写几个就可以发现核心是多次进行前缀和的操作,具体次数是k+1次
那么可以进行k+1次前缀和操作即可。
// Problem: E. Combinatorics Problem
// Contest: Codeforces - Educational Codeforces Round 148 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1832/problem/E
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int mod=998244353;
int n,x,y,m,k;
void output(vector<int> a)
{
for(int i=0,len=a.size();i<len;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
int main()
{
cin>>n;
vector<int> a(n);
cin>>a[0]>>x>>y>>m>>k;
for(int i=1;i<n;i++)
{
a[i]=(1ll*a[i-1]*x+y)%m;
}
for(int i=0;i<=k;i++)
{
for(int j=1;j<n;j++)
{
a[j]=(a[j]+a[j-1])%mod;
}
// output(a);
}
LL res=0;
for(int i=k-1;i<n;i++)
{
res^=1ll*(i+1)*a[i-k+1];
}
cout<<res<<endl;
return 0;
}