[cf] Educational Codeforces Round 126 (Rated for Div. 2)

前言

传送门 :

这场好难的样子,难指的是(会写,但是写不出来

A. Array Balancing

给定两个数组,完成多次操作操作后使得

∑ i = 1 n − 1 ( ∣ a i − a i + 1 ∣ + ∣ b i − b i + 1 ∣ ) \sum_{i=1}^{n-1}(|a_i-a_{i+1}|+|b_i-b_{i+ 1}|) i=1n1(aiai+1+bibi+1) 最小

操作描述 :

  • 选择一个下标 i i i
  • 交换 a i a_i ai b i b_i bi

看着像 d p dp dp,对于每个位置有交换和不交换两种选择

其实我们只需要模拟的时候 直接取 m i n min min即可

a b s ( a [ i ] − b [ i − 1 ] ) + a b s ( b [ i ] − a [ i − 1 ] ) ) abs(a[i]-b[i-1])+abs(b[i] - a[i-1])) abs(a[i]b[i1])+abs(b[i]a[i1]))表示前面的数进行了交换

Mycode

void solve(){
	cin>>n;

	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	ll sum = 0;
	for(int i=2;i<=n;i++){
		sum += min(abs(a[i] - a[i-1]) + abs(b[i]-b[i-1]),
		abs(a[i]-b[i-1])+abs(b[i] - a[i-1]));
	}
	
	 cout<<sum<<endl;
}

B. Getting Zero

给定一个数 v v v,询问最少的操作次数变为 0 0 0
操作描述如下 :

  • v = ( v + 1 )   m o d   32768 v=(v+1)\ mod\ 32768 v=(v+1) mod 32768
  • v = ( 2 ∗ v )   m o d   32768 v=(2*v)\ mod\ 32768 v=(2v) mod 32768

网上有很多 b f s bfs bfs d f s dfs dfs大佬在乱飞,所以我们考虑 d p dp dp

状态表示 :

d p [ i ] dp[i] dp[i]表示当前这个点到 0 0 0的最小次数

状态转移 :
d p [ j ] = m i n ( d p [ ( j + 1 ) % N ] , d p [ ( j ∗ 2 ) % N ] ) + 1 ; dp[j] = min(dp[(j+1)\%N],dp[(j*2)\%N])+1; dp[j]=min(dp[(j+1)%N],dp[(j2)%N])+1;

Mycode

void solve(){
	memset(dp,0x3f,sizeof dp);
	dp[0] = 0 ;
	
 
	for(int i=N-1; i >  0;  i -- ){
			for(int j = i ;j>0; j --  ){
				dp[j] = min(dp[(j+1)%N],dp[(j*2)%N])+1;
			}
	}

    cin >> n;
    for(int i=1;i<=n;i++){
    	int x;cin>>x;
    	cout<<dp[x]<<" ";
    }

}

C. Water the Trees

n n n棵树,每个数有一个高度 h [ i ] h[i] h[i],询问最少操作使得所有树的高度相同

操作如下 :

  • 如果是奇数天,可以给树浇水使得树 h [ i ] + 1 h[i]+1 h[i]+1
  • 如果是偶数天,可以给树浇水使得树 h [ i ] + 2 h[i]+2 h[i]+2
  • 你也可以不浇水
  • 一天只能浇一颗

看到这种最小的… 考虑直接 二分天数

首先所有数至少都要加到 m a x n maxn maxn里面

但是加到 m a x n maxn maxn不一定最优,因此我们还需要考虑 m a x n + 1 maxn+1 maxn+1, m a x n + 2 maxn+2 maxn+2

我们如何 c h e c k check check呢 ?

首先 :
+ 2 +2 +2的次数 m i d / 2 mid/2 mid/2
+ 1 +1 +1的次数 m i d − m i d / 2 mid-mid/2 midmid/2

然后我们计算每个 h [ i ] h[i] h[i],需要加多少次 2 2 2
然后再用计算出来的 t t 2 tt2 tt2,计算出需要 1 1 1的次数,即不足用 1 1 1

最后判断是否能补上即可

Mycode

const int N = 3e5+10;
ll a[N],n;
ll maxn ;

bool check(ll x,ll h){
	ll t2 =  x/2;
	ll t1 =  x - t2;
	
	for(ll i = 1;i<=n;i++){
		ll d =  h - a[i];
		ll tt2 = min(d/2,t2);
		
		d -= tt2*2;
		t2-= tt2;
		t1-=d;
	}
	return t1>=0;
}
void solve(){
	cin>>n;
	maxn = 0 ;
	
	for(ll i=1;i<=n;i++)cin>>a[i],maxn=max(a[i],maxn);
	
	
	ll l = 0 , r = 1e9*maxn;
	ll ans = 0  ;
	
	while(l <=r ){
		ll mid = (l+r)>>1;
		if(check(mid,maxn)||check(mid,maxn+1)||check(mid,maxn+2)){
			ans = mid;
			r = mid-1;
		}else l = mid+1;
	}
	cout<<ans<<endl;
	
	
}

D.Progressions Covering

给定 n n n k k k,以及数组 b [ ] b[] b[]

对空数组 a [ ] a[] a[]进行最少次操作使得 a [ i ] > = b [ i ] a[i]>=b[i] a[i]>=b[i]

操作描述如下 :
选择长度为 k k k的区间,使得
a [ 1 ] + 1 , a [ 2 ] + 2 , . . . , a [ k ] + k a[1]+1,a[2]+2,...,a[k]+k a[1]+1,a[2]+2,...,a[k]+k

我们可以把 a [ ] = b [ ] a[]=b[] a[]=b[]问题转换为 a [ ] a[] a[]中长度为 k k k 的序列减去一个 1.2.3.. k 1.2.3..k 1.2.3..k的等差序列,询问最少减去多少次使得所有数都 < = 0 <=0 <=0

对于每次做减去的操作,减去大于 0 0 0的数总是最优的,因此我们可以使用线段树维护一个差分序列即可

吐槽一下这题竟然有1000人过,感觉现在蓝名真的不配了QAQ

code

const int N = 3e5+10 ;
int n,k;

ll b[N];

struct node{
	int l,r;
	ll sum,add;
}tr[N*4];

void pushup(int u){
	tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void pushdown(int u){
	auto &root = tr[u];
	auto &left  = tr[u<<1];
	auto &right = tr[u<<1|1];

	if(root.add){
		left.add += root.add;
		left.sum += root.add*(left.r - left.l +1 );
		right.add+=root.add;
		right.sum+=root.add*(right.r - right.l +1);
		root.add = 0;

	}
}

void build(int u,int l,int r){
	if(l == r){
		tr[u] = {l,r,b[r] - b[r-1],0};
		return;
	}

	tr[u] = {l,r};
	int mid =(l+r)>>1;
	build(u<<1,l,mid);
	build(u<<1|1,mid+1,r);
	pushup(u);
}

void modify(int u,int l,int r,ll d){
	if(tr[u].l>=l && tr[u].r <= r){
		tr[u].sum += (ll)(tr[u].r - tr[u].l + 1)*d;
		tr[u].add +=d;
	}else{
		pushdown(u);
		int mid = (tr[u].l + tr[u].r)>>1;
		if(l<=mid)modify(u<<1,l,r,d);
		if(r>mid) modify(u<<1|1,l,r,d);
		pushup(u);
	}
}

ll query(int u, int l, int r){
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    ll sum = 0;
    if (l <= mid) sum = query(u << 1, l, r);
    if (r > mid) sum += query(u << 1 | 1, l, r);
    return sum;
}

void solve(){
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>b[i];
	build(1,1,n);
	ll res = 0 ;
	
	for(int i=n;i;i -- ){
		ll t = query(1,1,i);
		if(t<=0)continue;
		int last = min(k,i);
		ll add = (t+last-1)/last;
		res += add;
		modify(1,i-last+1,i,-add);
	}
	cout<<res<<endl;

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值