AcWing 第12场周赛题解

数组去重

从后往前扫一遍数组,找到未被标记的数,存下来,标记;已经被标记的就不用存下来

  • code
#include<iostream>
#include<cstring>
using namespace std;
int n;
const int maxn=55;
bool book[maxn*20];
int a[maxn];
int st[maxn],top=0;
int main(){
    int t;cin>>t;
    while(t--){
        cin>>n;memset(book,0,sizeof(book));
        for(int i=1;i<=n;++i) cin>>a[i];
        for(int i=n;i>=1;--i){
            if(book[a[i]]==0){
                book[a[i]]=1;
                st[++top]=a[i];
           }
        }
        cout<<top<<endl;
        while(top>=1) cout<<st[top--]<<" ";
        cout<<endl;
    }
    return 0;
}

构造字符串

字典序比较优先级
1.从左至右第一个不同字母的ASCII码大小关系
2.字符串长度

解释完后就可以看一下构造字符数组的要求

  1. 构成的字符串长度一定为k
  • 那这里就分两种情况讨论
    1.t比s长
    2.t比s短或相等
  1. t一定比s字典序大
  2. t字典序尽可能小
  3. 组成t的字母组成集合\(\in\)s字母构成的集合

2,3条件一合并,即为找字典序大于s的最小字符串

根据后面的条件
当 s比t长 时

先输出t,后面再加上集合中的最小字符

当 s比t短 时

根据字典序的定义,从头开始比较,满足字典序大于s那么就只需要有一个字符比当前位置的s的字符大,在这个位置前面的字符均相等即可

但又要满足最小字典序,所以我们在某个位置之后需要用最小字母补足长度

此时只需要从后往前找即可(改变的字母的个数越少越好,因为改变的越少,t越接近s)

  • code

#include<cstring>
#include<iostream>

using namespace std;
string s;
bool book[33];
int n,k;const int maxn=1e5+10;
int t;

int fd(int x){//找出大于s[x]的最小字母
	for(int i=0;i<=25;++i) 
		if(book[i] && 'a'+i>s[x]) return i;
	return s[x]-'a';   //找不到,自己便是最大字母
}
void output(int x,int mini) {//输出
	for(int i=1;i<x;++i) cout<<s[i];
	char c=fd(x)+'a';cout<<c;
	for(int i=x+1;i<=k;++i){
		c='a'+mini;
		cout<<c;
	}cout<<endl; 
}
int main(){
	cin>>t;
	while(t--){
		cin>>n>>k;string ans;
		memset(book,0,sizeof(book));
		cin>>s;
		for(int i=n;i>=1;--i)  s[i]=s[i-1];
		int len=n;
		int mini=33;
		for(int i=1;i<=len;++i){
			book[s[i]-'a']=1;
			mini=min(mini,s[i]-'a');
		}
		if(n<k){
			for(int i=1;i<=n;++i) cout<<s[i];
			for(int i=n+1;i<=k;++i) {
				char c='a'+mini;
				cout<<c;
			}cout<<endl;
			continue;
		}
		int p=1;
		for(int i=k;i>=1;--i){
		        if(fd(i)>s[i]-'a') {
				p=i;
				break;
			}
		}
		output(p,mini);
	}return 0;
}
 

环形数组

经典线段树操作

但数组是环形的,这个也好处理

  1. 当l<=r时 常规线段树操作
  2. 当r<l时 分为两段操作[l,n-1],[0,r]

区间加减后,值得相对大小不变,最小值还是最小值,直接+即可

输入时注意一下即可

  • code
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long ll;
ll inf=1e18-1;
const ll maxn=2e5+10;
ll ans[maxn<<2];
ll laz[maxn<<2];
int n,m;
int a[maxn];
void push_up(int p){
	ans[p]=min(ans[p<<1],ans[p<<1|1]);
}
void build(int p,int l,int r){
	if(l==r){
		ans[p]=a[l];return ;
	} 
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p); 
}
void add(int p,int pf,int l,int r){
	ans[p]+=laz[pf];
	laz[p]+=laz[pf];return ;
}
void push_down(int p,int l,int r){
	int mid=(l+r)>>1;
	add(p<<1,p,l,mid);
	add(p<<1|1,p,mid+1,r);
	laz[p]=0;
}
void update(int p,int l,int r,int nl,int nr,int k){
	if(nl<=l && r<=nr){
		ans[p]+=k;
		laz[p]+=k;
		return ;
	}
	int mid=(l+r)>>1;
	push_down(p,l,r);
	if(nl<=mid) update(p<<1,l,mid,nl,nr,k);
	if(nr>mid) update(p<<1|1,mid+1,r,nl,nr,k);
	push_up(p);
}
ll query(int p,int l,int r,int nl,int nr){
	if(nl<=l && r<=nr) return ans[p];
	ll ret=inf;int mid=(l+r)>>1;
	push_down(p,l,r);
	if(nl<=mid) ret=min(ret,query(p<<1,l,mid,nl,nr));
	if(nr>mid) ret=min(ret,query(p<<1|1,mid+1,r,nl,nr));	
	return ret; 
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;++i) scanf("%d",&a[i]);
	scanf("%d",&m);
	build(1,0,n-1);
	while(m--){
		int l,r,k;char c;bool book=0;
		scanf("%d %d%c",&l,&r,&c);
		if(c=='\n') book=1;
		else scanf("%d",&k);
		if(l<=r){
			if(book) printf("%lld\n",query(1,0,n-1,l,r));
			else update(1,0,n-1,l,r,k);
		}
		else {
			if(book) printf("%lld\n",min(query(1,0,n-1,l,n-1),query(1,0,n-1,0,r)));
			else {
				update(1,0,n-1,l,n-1,k);
				update(1,0,n-1,0,r,k); 
			}
		}
	}return 0;
}
posted @ 2021-08-16 16:03  归游  阅读(61)  评论(0编辑  收藏  举报