Codeforces Round #771 (Div. 2)

20220324

A.Reverse

找到首个不在自己位置的数翻转一下就可以了

// Problem: A. Reverse
// Contest: Codeforces - Codeforces Round #771 (Div. 2)
// URL: https://codeforces.com/contest/1638/problem/A
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
int p[550];
int a[550];
void so1ve(){
	mem(a,0);
	mem(p,0);
	int n;cin>>n;
	forr(i,1,n) {
		cin>>a[i];
		p[a[i]] = i;
	}
	forr(i,1,n){
		if(p[i] == i) continue;
		else{
			reverse(a+i,a+1+p[i]);
			break;
		}
	}
	forr(i,1,n) cout << a[i] <<" \n"[i==n];
}

B. Odd Swap Sort

只有当相邻两项为一个奇数与一个偶数才能交换位置,那么会发现奇/偶数之间的相对顺序不会改变,只需要判断奇数和偶数初始序列是否有序即可

bool judge(vector<int> s){
	int len = s.size();
	for(int i = 0; i < len-1;i++){
		if(s[i] > s[i+1]) return false;
	}
	return true;
}
void so1ve(){
	int n;cin>>n;
	vector<int> a,b;
	forr(i,1,n){
		int x;cin>>x;
		if(x&1) a.push_back(x);
		else b.push_back(x);
	}
	if(judge(a) && judge(b)) puts("Yes");
	else puts("No");
}

C. Inversion Graph

单调栈
可以用栈来维护每个连通块最大的值,答案为最后栈的元素个数

int stk[100005];
int tt;
void so1ve(){
	tt = 0;
	int n;cin>>n;
	vector<int> a(n+1);
	forr(i,1,n) cin>>a[i];
	stk[++tt] = a[1];
	forr(i,2,n){
		int t = a[i];
		while(tt && stk[tt] > a[i]){
			t = max(t,stk[tt]);
			tt--;
		}
		stk[++tt] = t;
	}
	cout << tt << endl;
}

D. Big Brush

bfs
找到最后一次刷上的数一定是四个相同一样的,标记为特殊的方块表示可以和其他任何颜色匹配,后用特殊的方块和周围的颜色进行匹配,若存在四个方块相同的 ( i , j ) (i,j) (i,j)放入队列中,结束后判断是否所有方块均变为特殊方块。

void so1ve(){
	int n,m;cin>>n>>m;
	vector<vector<int>> a(n+1);
	forr(i,1,n){
		a[i].resize(m+1);
		forr(j,1,m) cin>>a[i][j];
	}

	auto find = [&](int x,int y){
		int col = 0;
		if(x<=0||x>=n||y<=0||y>=m) return make_pair(false,col);
		set<int> s;
		for(auto it:{a[x][y],a[x+1][y],a[x][y+1],a[x+1][y+1]}){
			if(it != -1) s.insert(it),col = it;
		}
		return make_pair(s.size()==1,col);
	};
	
	
	auto paint = [&](int x,int y){
		a[x][y] = a[x+1][y] = a[x][y+1] = a[x+1][y+1] = -1;
	};
	
	vector<tuple<int,int,int>> ans;
	queue<tuple<int,int,int>> q;
	
 	forr(i,1,n-1)forr(j,1,m-1){
 		auto [res, col] = find(i,j);
 		if(res){
 			q.push({i,j,col});
 			ans.push_back({i,j,col});
 			paint(i,j);
 		}
 	}
	
	while(!q.empty()){
		auto [x,y,col] = q.front();
		q.pop();
		for(int i = x-1;i <= x+1;i++)
			for(int j = y-1;j <= y+1;j++){
				auto [res,col] = find(i,j);
				if(res){
		 			q.push({i,j,col});
		 			ans.push_back({i,j,col});	
		 			paint(i,j);					
				}
			}
		}
	
	forr(i,1,n)forr(j,1,m){
		if(a[i][j] != -1) {
			puts("-1");
			return;
		}
	}
	
	reverse(ans.begin(),ans.end());
	cout << ans.size() << endl;
	for(auto [x,y,z]:ans){
		cout << x <<" " << y <<" "<< z << '\n';
	}
}

E. Colorful Operations

线段树
3 3 3个操作,每个元素有价值和颜色两个属性
1.将 [ L , R ] [L,R] [L,R]的颜色变为 c c c
2.将颜色 c c c 的值增加 x x x
3.输出 a i a_i ai 的值

通过延迟标记 l a z y [ ] lazy[] lazy[] 数组来计算一个元素增加价值
线段树里用 l z lz lz 变量来维护其值的变化
查询 a i a_i ai 时为 a s k ( 1 , i ) + l a z y [ c o l [ i ] ] ask(1,i) + lazy[col[i]] ask(1,i)+lazy[col[i]];

对于操作1,利用区间修改来对颜色进行修改
这里 l z lz lz 标记的变化为

		t[u].lz += lazy[t[u].col];
		t[u].col = c;
		t[u].lz -= lazy[t[u].col];

因为如果当前 i i i 的颜色为 c 1 c_1 c1 ,要变为颜色 c 2 c_2 c2 ,因为若颜色不发生改变查询时会加上通过 2 2 2操作 c 1 c_1 c1 增加的量,但是现在颜色已经改变为 c 2 c_2 c2 最后查询的时候就会去加上 c 2 c_2 c2 的增量,所以 l z lz lz要先加上 l a z y [ c 1 ] lazy[c_1] lazy[c1] ,此增量已经对i有影响,同时要前去变为 c 2 c_2 c2之前已经对颜色 c 2 c_2 c2的值造成影响的 l a z y lazy lazy,不减去的话最后查询时会使i 还未变成 c 2 c_2 c2 前对 c 2 c_2 c2的增量加到 i i i 的值

code

int n,m;
struct tree{
    int l,r;
	int col;
	int lz;
	bool sam; // 为护两个子区间的颜色是否相同
}t[N<<2];
int lazy[1000005];
void up(int i){
	//  儿子颜色要相同且儿子的儿子也要颜色相同才能传值
	if(t[(i<<1)].sam && t[(i<<1|1)].sam && t[(i<<1)].col == t[(i<<1|1)].col){
		t[i].col = t[i<<1].col;
		t[i].sam = true;
	}
	否则就是儿子不相同
	else t[i].sam = false;

}
void down(int i){
    if(!t[i].sam) return;
	t[i<<1].lz += t[i].lz;
	t[i<<1|1].lz += t[i].lz;
	t[i<<1].col  = t[i].col;
	t[i<<1|1].col = t[i].col;
	t[i].lz = 0;
}

void build(int i, int l, int r) {
	t[i].l = l, t[i].r = r;
	t[i].sam = true, t[i].col = 1;
	t[i].lz = 0;
	if (l == r)
		return;
	int mid = (l + r) >> 1;
	build(i << 1, l, mid), build(i << 1 | 1, mid + 1, r);
	up(i);
}


void change(int u,int l,int r,int c){

	if (t[u].l > r || t[u].r < l)
		return;

	// 当前区间的儿子要颜色相同才能改值,是因为不同颜色的lz变化不同
	if (t[u].l >= l && t[u].r <= r && t[u].sam) {
		t[u].lz += lazy[t[u].col];
		t[u].col = c;
		t[u].lz -= lazy[t[u].col];
		return;
	}//延迟修改了每个数的值,只有当这个数的颜色发生变化时,才修改这个数的值

	int mid = (l + r) >> 1;
	down(u);
	change(u << 1, l, r, c), change(u << 1 | 1, l, r, c);
	up(u);
}

int ask(int u, int x) {
	if (t[u].l == x &&  t[u].r == x)
		return t[u].lz + lazy[t[u].col];

	down(u);
	int mid = t[u].l + t[u].r >> 1;
	if (x <= mid)
		return ask(u << 1, x);
	else
		return ask(u << 1 | 1, x);
}

string s;
int l,r,col;
int val;
int x;
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	build(1,1,n);
	while(m--){
		cin>>s;
		if(s == "Color"){
			cin>>l>>r>>col;
			change(1,l,r,col);
		}
		else if(s == "Add"){
			cin>>col>>val;
			lazy[col] += val;
		}else{
			cin>>x;
			cout << (ask(1,x)) << endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值