CF1291
CF1291A
个人做法:如果是偶数就把末尾的偶数全删了 数位加起来不满足的话找个奇数删了就行
然后没判前导0 WA了3发
实际上找两个奇数直接输出即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define gc getchar
#define pc putchar
const int N=3e5+5;
const int M=1e5+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-8;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int t,n,m,a[N],sum,flag,frt;
char c;
signed main(){
t=read();
while(t--){
n=read();sum=flag=0;frt=1;
for(int i=1;i<=n;i++){
scanf(" %c",&c);
a[i]=c-'0';
}
while(n&&!(a[n]&1))n--;
if(!n){puts("-1");continue;}
for(int i=1;i<=n;i++)sum+=a[i];
if(sum&1){
for(int i=n-1;i;i--)
if(a[i]&1){flag=i;break;}
if(!flag){puts("-1");continue;}
if(n==1){puts("-1");continue;}
}
for(int i=1;i<=n;i++){
if(i==flag)continue;
if(frt&&!a[i])continue;
write(a[i]);frt&=!(bool)a[i];
}
if(frt){puts("0");continue;}
puts("");
}
return 0;
}
CF1291B
贪心。
发现如果原来中间位置就满足要求 右移后右边一定也满足 那么对于两边都贪心的多选
且对于左边 只要每个位上的数分别比 0,1,2...大 那么减到该状态一定最优 右边同理
最后判一下两侧满足区间是否有交集即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define gc getchar
#define pc putchar
const int N=3e5+5;
const int M=1e5+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-8;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int t,n,m,a[N],l,r;
char c;
signed main(){
t=read();
while(t--){
n=read();l=0,r=n+1;
for(int i=1;i<=n;i++)a[i]=read();
while(l+1<=n&&a[l+1]>=l)l++;
while(r-1>=1&&a[r-1]>=n-r+1)r--;
puts(l>=r?"YES":"NO");
}
return 0;
}
CF1291C
首先 我们只看自己前面的 \(m-1\) 人
我们可以枚举自己操纵的人有几个取前面 对单个情况的最坏答案取max
单个情况也可直接枚举 暴力 \(O(n^2)\) 可以过
发现每种情况都会有状态被重复取过 可以单调队列优化到 \(O(n)\)
但 \(n<=3500\) 写啥正解啊 暴力不香吗
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define gc getchar
#define pc putchar
const int N=3e5+5;
const int M=1e5+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-8;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int t,n,m,k,a[N],ans,res;
char c;
signed main(){
t=read();
while(t--){
n=read();m=read();k=read();ans=0;
for(int i=1;i<=n;i++)a[i]=read();
if(k>=m-1){
for(int i=1;i<=m;i++)ans=max(ans,a[i]);
for(int i=n;i>=n-m+1;i--)ans=max(ans,a[i]);
writel(ans);continue;
}
for(int i=1;i<=k+1;i++){
res=inf;
for(int j=i;j<=i+m-k-1;j++){
res=min(res,max(a[j],a[j+n-m]));
}
ans=max(ans,res);
}
writel(ans);
}
return 0;
}
CF1291D
看完题解才知道这是构造+结论题。
首先这操作相当于把原串打乱 使无论咋分段每段元素和原来比一定有至少一个不同
考虑几种情况:
- \(l=r\) 一定可以(一个没法分)
- \(s[l]\not=s[r]\) 把 \(s[l]\) 放到最左面 \(s[r]\) 放到最右 一定满足
- 否则看区间字母数量 \(cnt\ge 3\) 一定满足
证明:设此时 \(s[l]=s[r]=x\) 其他位置必有 \(y\ z\)(第二条不满足)
那么把 \(y\) 放最左 \(z\) 放最右
这样想让两种元素一起出现 必须要把整个串全选上 这样就不满足要求
剩下情况puts(“No”)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define gc getchar
#define pc putchar
const int N=3e5+5;
const int M=1e5+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-8;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int t,n,m,k,a[N],ans,res,sum[N][30],q,l,r;
char s[N];
signed main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=26;j++)
sum[i][j]=sum[i-1][j]+(s[i]-'a'+1==j);
q=read();
while(q--){
l=read();r=read();
if(l==r){puts("Yes");continue;}
if(s[l]^s[r]){puts("Yes");continue;}
int cnt=0;
for(int i=1;i<=26;i++)cnt+=(bool)(sum[r][i]-sum[l-1][i]);
if(cnt>=3)puts("Yes");
else puts("No");
}
return 0;
}
CF1291E
题解看好久才看懂 果然还是太蒟了
重要性质:任意三个子集的交集为空集
即每个位置最多出现两次
考虑对于每个位置:
- \(s[i]=0\) 两次操作任选其一(不能都选 也不能不选)
- \(s[i]=1\) 两次操作必须都选/都不选
(不会一次操作做两次 这操作相当于异或 两次就白做了)
考虑用扩展域并查集维护这个操作 一层是选当前操作 一层不选
- \(s[i]=0\) A操作选--> B操作不选 A操作不选--> B操作选
- \(s[i]=1\) A操作选--> B操作选 A操作不选--> B操作不选
那么对于只出现一次的操作呢
建立虚拟操作0:
- \(s[i]=0\) A操作选--> 0操作不选 A操作不选--> 0操作选
- \(s[i]=1\) A操作选--> 0操作选 A操作不选--> 0操作不选
0操作选不了 最后让0强制不选才是正确答案
合并时维护每个联通块上下两层的大小 维护合并到当前位置 \(ans\) 大小 两层都算了除2即可
(如果第一层小 那么答案就选0了 换成第二层不选0的即可)
这题细节比较多 预处理要带0 操作数需要++
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define gc getchar
#define pc putchar
const int N=1e6+5;
const int M=1e5+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-8;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int n,k,c,fa[N],col[N][2],siz[N][2],x,ans;
char s[N];
inl int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inl void merge(int x,int y){
x=find(x),y=find(y);
if(x==y)return;
ans-=min(siz[x][0],siz[x][1])+min(siz[y][0],siz[y][1]);
fa[y]=x;siz[x][0]+=siz[y][0];siz[x][1]+=siz[y][1];
ans+=min(siz[x][0],siz[x][1]);
}
signed main(){
n=read();k=read()+1;
scanf("%s",s+1);
for(int i=0;i<=(k<<1);i++){
fa[i]=i;
siz[i][i>=k]=1;
}
for(int i=1;i<k;i++){
c=read();
while(c--){
x=read();
col[x][(bool)col[x][0]]=i;
}
}
for(int i=1;i<=n;i++){
if(s[i]-'0'){
merge(col[i][0],col[i][1]);
merge(col[i][0]+k,col[i][1]+k);
}else{
merge(col[i][0],col[i][1]+k);
merge(col[i][1],col[i][0]+k);
}
int x=find(0);
if(siz[x][0]>siz[x][1])writel(ans>>1);
else writel((ans>>1)-siz[x][0]+siz[x][1]);
}
return 0;
}
CF1291F
不会。