Codeforces Round #742 (Div. 2)
作者:互联网
MEXor Mixup
m e x mex mex意思就是不在集合里面最小的正整数.所以给出一个 m e x mex mex那么他前面的所有的东西都是要选上的,然后前面的这些所有的异或之和(假设为 x x x)就可以预处理然后用 O 1 O1 O1的计算出来,因为给出的必须异或和为 y y y,所以我们还差一个 z = x x o r y z=x\ xor \ y z=x xor y,也就是 z x o r x = y z \ xor \ x=y z xor x=y,然后就是分情况了:
- z = m e x z=mex z=mex就需要额外的两个数才行,因为不能取 m e x mex mex这个值嘛,不然 m e x mex mex就会变
- z > m e x z>mex z>mex或者 0 < z < m e x 0<z<mex 0<z<mex,注意元素是可以重复的,所以就再要一个 z z z就行
- z = 0 z=0 z=0,就不需要额外的了
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 400000
using namespace std;
int T,a[maxn],x,y;
signed main(){
scanf("%d",&T);
for(int i=1;i<=300000;i++) a[i]=a[i-1]^i;
while(T--){
scanf("%d %d",&x,&y);
if(a[x-1]==y) {printf("%d\n",x);continue;}
int temp=a[x-1]^y;
if(temp>x) printf("%d\n",x+1);
else if(temp==x) printf("%d\n",x+2);
else printf("%d\n",x+1);
}
return 0;
}
Carrying Conundrum
这个题目的进位方式有一点奇怪,他是进到下下位去的.
- 一开始的想法是:用 d p dp dp来转移,然后 d p [ i ] [ 1 / 0 ] dp[i][1/0] dp[i][1/0]表示第 i i i位是否进位得到的答案,然后转移就是很麻烦,和这一位,下下位都有关系,唯独下一位和这个没什么关系,所以就感觉像是有什么该转移的没有转移.
- 于是我们就发现把单双数位单独的列出来就行了,这样子看的话就是正常的进位,也就是说我们如果要让和等于 15005 15005 15005的话,就拆成 105 105 105和 50 50 50就好了,然后答案就是 106 ∗ 51 − 2 106*51-2 106∗51−2.
- 因为这种拆法使得进位变成正常的十进制,所以有:
106 = 0 + 106 = 1 + 105 . . . . . . . . . . . . . = 106 + 0 106=0+106\\ \ \ \ \ \ \ =1+105\\.............\\ \ \ \ \ \ \ =106+0 106=0+106 =1+105............. =106+0
- 所以一共有 107 107 107种,同理 51 51 51就一共有 52 52 52种,所以乘法原理就是 106 ∗ 51 106*51 106∗51,只不过还要减去 2 2 2,就是因为不能 0 + 15005 = 15005 0+15005=15005 0+15005=15005这种情况.
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 50
#define int long long
using namespace std;
int T,n,dp[maxn],a[maxn];
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);
int k=0; memset(dp,0,sizeof dp);
while(n){
a[++k]=n%10;
n/=10;
}
int temp1=0,temp2=0;
for(int i=k;i;i--){
if(i%2) temp1=temp1*10+a[i];
else temp2=temp2*10+a[i];
}
if(temp1==0 || temp2==0) printf("%lld\n",temp1+temp2-1);
else{
printf("%lld\n",(temp2+1)*(temp1+1)-2);
}
}
return 0;
}
Expression Evaluation Error
- 因为是改了之后是十一进制,所以高位尽量保持不变,用低位来保持加和的一样.
- 然后 5000 5000 5000分成个 5 5 5个的话,虽然要让高位保持不变,但是 500 0 11 = 100 0 11 ∗ 5 5000_{11}=1000_{11}*5 500011=100011∗5,这样来看的话因为要拆成很多个,所以在保持最大的情况下每一个拆得越小越好,才让后面有东西可以拆,像上面那样拆就很好,虽然 500 0 11 = 200 0 11 + 300 0 11 5000_{11}=2000_{11}+3000_{11} 500011=200011+300011也行,但是明显少了三个可以拆出来的.
- 假设 5000 5000 5000分成 6 6 6个的话,先分成 1000 , 1000 , 1000 , 1000 1000,1000,1000,1000 1000,1000,1000,1000,现在剩下 1000 1000 1000,但是要分成两个,所以将 1000 1000 1000再先分成 100 100 100,最后只剩下一个了,那就是 5000 5000 5000减去所有之前的,也就是 900 900 900.至于为什么知道 1000 1000 1000分成两个时候是 100 100 100,这个就是和向下取整有点类似的操作,见代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int T,n,m;
signed main(){
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&m);
for(int i=1;i<m;i++){
double temp=pow(10,(int)log10(n-(m-i)));
printf("%.0f ",temp); n-=temp;
}
printf("%d\n",n);
}
return 0;
}
Non-Decreasing Dilemma
单点修改,区间查询,比较显然是要用数据结构的,这里要用到线段树合并,以前如果没有做过类似的题目的话是比较难想到的.
- 先假设区间 [ a , b ] [a,b] [a,b]为 l l l,区间 [ b + 1 , c ] [b+1,c] [b+1,c]为 r r r,并假设现在已经处理好了 l , r l,r l,r这两个区间了,所以只用考虑如何合并这两个区间,更新到 [ a , c ] [a,c] [a,c],因为在叶子节点的时候,只有一个数的时候是已知答案的,然后慢慢向上更新,所以假设只考虑怎么合并更新
- 设置几个变量: l m a x lmax lmax表示从左边第一个数字开始有几个数字是连续上升的, r m a x rmax rmax表示从右边开始最多有几个向左的是连续下降的,如 [ a , b ] = [ 1 , 2 , 3 , 5 , 7 , 1 , 5 , 2 , 3 , 4 ] [a,b]=[1,2,3,5,7,1,5,2,3,4] [a,b]=[1,2,3,5,7,1,5,2,3,4]是 l m a x = 5 , r m a x = 3 lmax=5,rmax=3 lmax=5,rmax=3. l v a l lval lval表示区间最左边那个数字是多少, r v a l rval rval表示最右边那个数是多少,还有一个 s u m sum sum也就是区间的所求答案是多少,还有 l e n len len,字面意思.
u.lval=l.lval; u.rval=r.rval;
u.lmax=l.lmax; u.rmax=r.rmax;
u.len=l.len+r.len; u.sum=l.sum+r.sum;
- 上面这段代码我认为还是挺显然的,毕竟是合并嘛
- 假设 l = [ 1 , 2 , 3 , 4 ] l=[1,2,3,4] l=[1,2,3,4],然后 r = [ 1 , 2 , 3 , 4 ] r=[1,2,3,4] r=[1,2,3,4]的话,就是上面那点点代码就行了,但是如果 r = [ 5 , 6 , 7 ] r=[5,6,7] r=[5,6,7]的话,就出现问题了,这两个合起来的 s u m sum sum不等于两个的直接相加,因为中间是可以连起来的比如可以有 [ 4 , 5 ] [4,5] [4,5],现在的 s u m sum sum还得再加上 l . r m a x ∗ r . l m a x l.rmax*r.lmax l.rmax∗r.lmax,也就是乘法原理,左边的选一个,再在右边选一个,跨过来,这样的话答案就统计完全了.
- 还有 l m a x lmax lmax和 r m a x rmax rmax也得更新,像上面的那个区间情况就是 l . l e n = l . m a x l.len=l.max l.len=l.max的情况,也就是整个区间都递增,所以 r r r的左边的数如果是递增的话, u u u的 l m a x lmax lmax就会边长,就是 l . l e n + r . l m a x l.len+r.lmax l.len+r.lmax,然后 r m a x rmax rmax同理
其他的就简单了,注意 q u e r y query query得用 n o d e node node形式,因为答案要用 m e r g e merge merge得到的答案,而不是 l . s u m + r . s u m l.sum+r.sum l.sum+r.sum.
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 200010
#define int long long
using namespace std;
int n,q,a[maxn];
struct node{
int sum,lmax,rmax,len,lval,rval;
}tree[maxn<<2];
void modify(node &x,int temp){
x.len=x.sum=x.lmax=x.rmax=1;
x.lval=x.rval=temp;
}
node merge(node l,node r){
node u;
u.lval=l.lval; u.rval=r.rval;
u.lmax=l.lmax; u.rmax=r.rmax;
u.len=l.len+r.len; u.sum=l.sum+r.sum;
if(l.rval<=r.lval){
if(l.lmax==l.len) u.lmax+=r.lmax;
if(r.rmax==r.len) u.rmax+=l.rmax;
u.sum+=l.rmax*r.lmax;
}
return u;
}
void build(int id,int l,int r){
if(l==r) {modify(tree[id],a[l]); return ;}
build(id*2,l,l+r>>1),build(id*2+1,(l+r>>1)+1,r);
tree[id]=merge(tree[id*2],tree[id*2+1]);
}
void update(int id,int l,int r,int x,int val){
if(l==r) {modify(tree[id],val); return ;}
int mid=l+r>>1;
if(mid>=x) update(id*2,l,mid,x,val);
else update(id*2+1,mid+1,r,x,val);
tree[id]=merge(tree[id*2],tree[id*2+1]);
}
node query(int id,int l,int r,int ll,int rr){
if(l==ll && r==rr) return tree[id];
int mid=l+r>>1;
if(mid>=rr) return query(id*2,l,mid,ll,rr);
else if(ll>mid) return query(id*2+1,mid+1,r,ll,rr);
else return merge(query(id*2,l,mid,ll,mid),query(id*2+1,mid+1,r,mid+1,rr));
}
signed main(){
scanf("%lld %lld",&n,&q);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);
for(int i=1,opt,l,r;i<=q;i++){
scanf("%lld %lld %lld",&opt,&l,&r);
if(opt==1) update(1,1,n,l,r);
if(opt==2) printf("%lld\n",query(1,1,n,l,r).sum);
}
return 0;
}
题解发的很晚,见谅,快开学了,更新的比较慢,但是我会努力的.
标签:742,int,sum,rmax,Codeforces,lmax,Div,id,1000 来源: https://blog.csdn.net/qq_44590230/article/details/120296445