ARC136
A 把所有 A 换 BB,再贪心换回 A
#include <cstdio>
using namespace std;
char s[200003];
int n;
int main(){
scanf("%d",&n);
scanf("%s",s+1);
bool fl=0;
for(int i=1;i<=n;++i){
if(s[i]=='B'){
if(fl) putchar('A'),fl=0;
else fl=1;
}
else if(s[i]=='A') putchar('A');
else{
if(fl) putchar('B'),fl=0;
putchar(s[i]);
}
}
if(fl) putchar('B');
putchar('\n');
return 0;
}
B 考虑对于排列怎么做:一次操作不改变逆序对奇偶性,可以证明这个条件充分。可以考虑将每一个 \(i\in [1,n-2]\) 放回原位,如果位置奇偶性不同,那么就让 i 的后两位先往前跳一次,发现只有最后两个数无法调整,而这两个数的位置关系由逆序对奇偶性可以决定
现在可能有重复数字,那么钦定这两个数字为排列最大和次大,那么这两个数一定可以被丢后面,一定可以满足条件
那么判掉重复数字再跑逆序对即可
#include <cstdio>
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int _=5003;
int a[_],b[_],c[_],n;
void upd(int x){for(int i=x;i<=5000;i+=(i&-i))++c[i];}
int qry(int x){int r=0;for(int i=x;i;i-=(i&-i))r+=c[i];return r;}
int cnta[_],cntb[_];
int main(){
n=read();
for(int i=1;i<=n;++i) ++cnta[a[i]=read()];
for(int i=1;i<=n;++i) ++cntb[b[i]=read()];
for(int i=1;i<=5000;++i)
if(cnta[i]^cntb[i]){
puts("No");
return 0;
}
for(int i=1;i<=5000;++i)
if(cnta[i]>1||cntb[i]>1){
puts("Yes");
return 0;
}
bool res=0;
for(int i=1;i<=5000;++i) c[i]=0;
for(int i=1;i<=n;++i){
upd(a[i]);
res^=(i-qry(a[i]))&1;
}
for(int i=1;i<=5000;++i) c[i]=0;
for(int i=1;i<=n;++i){
upd(b[i]);
res^=(i-qry(b[i]))&1;
}
if(res) puts("No");
else puts("Yes");
return 0;
}
C 观察发现可以在最小值前面一个位置破环成链跑序列上的原问题,可以笛卡尔树解决,但是有一个操作就不能实现,类似于操作 \(\{1,2,3,4,5\},\{5,6,1,2\}\) 这两个序列
发现这样的操作等价于 \(\{1,2,3,4,5,6\},\{1,2\},\{5\}\) ,发现其作用效果是对整个环操作一次,再把两个不相交的区间操作合并
相当于我们有 \(\min_{i=1}^n a_i\) 次合并不相交区间的机会,这个在处理笛卡尔树时顺便减掉
#include <cstdio>
#include <algorithm>
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
typedef long long ll;
const int _=400003;
int n,a[_],mn; ll res;
int cmp(int i,int j){return a[i]<a[j]?i:j;}
int f[20][_],lg[_];
int qry(int l,int r){
int k=lg[r-l+1]-1;
return cmp(f[k][l],f[k][r-(1<<k)+1]);
}
ll solve(int l,int r,ll cur){
if(l>r) return 0;
int p=qry(l,r);
ll lc=solve(l,p-1,a[p]),rc=solve(p+1,r,a[p]);
ll tt=min({lc,rc,res});
res-=tt;
return lc+rc+a[p]-cur-tt;
}
int main(){
n=read();mn=0x7fffffff;
for(int i=1;i<=n;++i) a[i]=read(),mn=min(mn,a[i]);
for(int i=n+1;i<=n+n;++i) a[i]=a[i-n];
for(int i=1;i<=n+n;++i) f[0][i]=i,lg[i]=lg[i>>1]+1;
for(int t=1;t<20;++t)
for(int i=1;i+(1<<t)-1<=n+n;++i)
f[t][i]=cmp(f[t-1][i],f[t-1][i+(1<<(t-1))]);
int l=1,r=n;res=mn;
while(a[r]^mn) ++r;
while(a[l]^mn) ++l;
printf("%lld\n",solve(l,r,0));
return 0;
}
赛时只做到这……太慢了
D 能与数 \(a\) 相加还不进位的数就是满足十进制每一位对应小于 \(999999-a\) 的十进制每一位,这是一个高维前缀和的形式,直接做
#include <cstdio>
using namespace std;
const int _=1000003;
int cnt[_],a[_],n;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
int main(){
n=read();
for(int i=1;i<=n;++i) ++cnt[a[i]=read()];
for(int i=1;i<1000000;++i)
if(i%10) cnt[i]+=cnt[i-1];
for(int i=10;i<1000000;++i)
if(i/10%10) cnt[i]+=cnt[i-10];
for(int i=100;i<1000000;++i)
if(i/100%10) cnt[i]+=cnt[i-100];
for(int i=1000;i<1000000;++i)
if(i/1000%10) cnt[i]+=cnt[i-1000];
for(int i=10000;i<1000000;++i)
if(i/10000%10) cnt[i]+=cnt[i-10000];
for(int i=100000;i<1000000;++i)
if(i/100000%10) cnt[i]+=cnt[i-100000];
long long res=0;
for(int i=1;i<=n;++i){
res+=cnt[999999-a[i]];
while(a[i]){
if(a[i]%10>4) {++res;break;}
a[i]/=10;
}
--res;
}
printf("%lld\n",res/2);
return 0;
}
感觉这道题比 C 简单很多(主要是太板)
E 妙题,看了题解才会
进行几个关键的观察:偶数之间全部可达
考虑怎么处理奇数,发现对于一个奇数 \(x\) ,设 \(v_x\) 表示最小质因子,那么第一个能到他的节点是 \(x-v_x\) ,第一个他能到的节点为 \(x+v_x\),这两个数都是偶数
也就是说设一个数往前第一次能到的偶数为 \(L_i\)(如果是偶数就是本身) ,往后第一次能到的奇数为 \(R_i\)(如果是偶数就是本身)
那么只要 \(R_x\leq L_y(x<y)\) ,\(x\) 就能到 \(y\)
现在让我们选一个全部相交的集合,那么这个集合至少交于一个点,用区间覆盖差分统计答案即可
#include <cstdio>
using namespace std;
#define gec (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
int read(){
char c=gec;int x=0;
while(c<48||c>57) c=gec;
do x=(x<<1)+(x<<3)+(c^48),c=gec;
while(c>=48&&c<=57);
return x;
}
const int N=1000003;
const int K=78500;
int n,a[N],v[N];
int pr[K],m;
long long f[N*2],res;
int main(){
n=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=2;i<=n;++i){
if(!v[i]) v[i]=pr[++m]=i;
for(int j=1;j<=m&&pr[j]*i<=n;++j){
v[i*pr[j]]=pr[j];
if(i%pr[j]==0) break;
}
}
res=f[1]=a[1];
for(int i=2;i<=n;++i)
if(i&1){
f[i-v[i]+1]+=a[i];
f[i+v[i]]-=a[i];
}
else{
f[i]+=a[i];
f[i+1]-=a[i];
}
for(int i=1;i<=2*n;++i){
f[i]+=f[i-1];
if(f[i]>res) res=f[i];
}
printf("%lld\n",res);
return 0;
}