Codeforces Round #816 (Div. 2)

C. Monoblock

题意: 给定一个长度为 n n n的序列。定义 g ( l , r ) g(l,r) g(l,r) a [ l : r ] a[l:r] a[l:r]中最长数块的个数,数块的定义为一个区间内的数完全相同。现有 m m m个操作,每次操作给定两个整数 p , x p,x p,x,表示从此刻起,将 a [ p ] a[p] a[p]修改为 x x x(注意修改是永久的,这里看错了调了好久嘤嘤嘤),对于每次操作后,求 ∑ l = 1 n ∑ r = l n g ( l , r ) \sum\limits_{l = 1}^n \sum\limits_{r = l}^n g(l, r) l=1nr=lng(l,r)
题解: 很容易维护出原始的 ∑ l = 1 n ∑ r = l n g ( l , r ) = s u m \sum\limits_{l = 1}^n \sum\limits_{r = l}^n g(l, r) = sum l=1nr=lng(l,r)=sum,对于每次操作,先将原 a [ p ] a[p] a[p]在答案中产生的贡献去除,然后将 a [ p ] a[p] a[p]修改为 x x x,在将 x x x在序列中产生的贡献加上。
a [ p ] a[p] a[p]在序列中的贡献为:若 a [ p ] = = a [ p − 1 ] a[p]==a[p-1] a[p]==a[p1],则没有贡献;若 a [ p ] ! = a [ p − 1 ] a[p]!=a[p-1] a[p]!=a[p1],则贡献为 ( p − 1 ) ∗ ( n − p + 1 ) (p-1)*(n-p+1) (p1)(np+1)。若 a [ p ] = = a [ p + 1 ] a[p]==a[p+1] a[p]==a[p+1],则没有贡献;若 a [ p ] ! = a [ p + 1 ] a[p]!=a[p+1] a[p]!=a[p+1],则贡献为 p ∗ ( n − p ) p*(n-p) p(np)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100100;
int n,m,a[N];
ll cnt[N],s[N];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		cnt[i]=cnt[i-1];
		if(a[i]!=a[i-1]) cnt[i]++;
	}
	for(int i=n;i>=1;i--) s[i]=s[i+1]+cnt[i];
	ll sum1=0,sum2=0,sum3=0,sum;
	for(int i=1;i<=n;i++) sum1+=s[i];
	for(int i=1;i<=n;i++) sum2+=1ll*(n-i+1)*cnt[i];
	for(int i=1;i<=n;i++) sum3+=n-i+1;
	sum=sum1-sum2+sum3;
	int p,x;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&p,&x);
		if(a[p]!=a[p-1]) sum-=1ll*(p-1)*(n-p+1);
		if(a[p]!=a[p+1]) sum-=1ll*p*(n-p);
		a[p]=x;
		if(x!=a[p-1]) sum+=1ll*(p-1)*(n-p+1);
		if(x!=a[p+1]) sum+=1ll*p*(n-p);
		printf("%lld\n",sum);
	}
	return 0;
}

D. 2+ doors

题意: 已知序列长度为 n n n,现给 m m m组描述该序列,每组描述给定三个正整数 i , j , x i,j,x i,j,x,表示 a [ i ] ∣ a [ j ] = = x a[i]|a[j]==x a[i]a[j]==x,求字典序最小的序列。
题解: 一开始一直在想每一位的 1 1 1,但是或操作下 1 1 1有很大的不确定性,所以跑偏了
或操作每位相对独立,故按位考虑。首先将每位都置 1 1 1,若两个数相或结果为 0 0 0,那么将该位置$0。之后再每位从 1 1 1 n n n(保证字典序最小)的考虑该位 1 1 1能否删去,即可得到字典序最小的序列。
ps: 注意运算符的优先级,调了好久嘤嘤嘤。

#include<bits/stdc++.h>
using namespace std;
const int N=100100;
int n,m,ans[N];
bool vis[N];
vector< pair<int,int> >ve[N];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) ans[i]=(1<<30)-1;
	for(int i=1,a,b,c;i<=m;i++){
		scanf("%d%d%d",&a,&b,&c);
		if(a==b){
			ans[a]=c;
			vis[a]=1;
			continue;
		}
		ve[a].push_back(make_pair(b,c));
		ve[b].push_back(make_pair(a,c));
		for(int j=0;j<30;j++){
			if((c&(1<<j))==0){
				if(ans[a]&(1<<j)) ans[a]^=(1<<j);
				if(ans[b]&(1<<j)) ans[b]^=(1<<j);
			}
		}
	}
	for(int k=0;k<30;k++){
		for(int i=1;i<=n;i++){
			if(vis[i]) continue;
			if((ans[i]&(1<<k))==0) continue;
			int flag=1;
			for(int j=0;j<ve[i].size();j++){
				int a=i,b=ve[i][j].first,x=ve[i][j].second;
				if((x&(1<<k))&&(ans[b]&(1<<k))==0) flag=0;
			}
			if(flag) ans[i]^=(1<<k);
		}
	}
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值