[cf] Codeforces Round #783 (Div. 2)

前言

传送门 :

A.

题意 :
给你 n , m n,m n,m 询问从 1 , 1 1,1 1,1往四个方向走,不能连续选择一条直线上的方向(以为是一个方向去世,问最少步数

思路 :
显然对于 ( 1 , 1 ) − > ( x , x ) (1,1)->(x,x) (1,1)>(x,x)这种一定是 ( x − 1 ) ∗ 2 (x-1)*2 (x1)2的,然后我们补上一下后面即可

画个图会发现,偶数的时候需要多走 d + 1 d+1 d+1,奇数多走 d d d

void solve(){
	ll a,b;cin>>a>>b;
	if(a == 1 && b >= 3  || b== 1 && a >= 3){
		cout<<-1<<endl;
		return;
	}
	
	int d = abs(a-b);
	if(d&1)
	d = d*2 -1 ;
	else
	d = d*2;
	int  minn = min(a,b);
	
	cout<<abs(minn-1)+abs(minn-1)+d<<endl;
	
}
 

B.

题意 :
n n n个人 m m m个凳子, i i i人有 a [ i ] a[i] a[i]的要求,要求他做的位置两边都要有 a [ i ] a[i] a[i]个空凳子
询问是否可行

思路 :

考虑成,顺序排列,因此总需要

( ∑ i = 1 n a [ i ] ) + n (\sum_{i=1}^na[i])+n (i=1na[i])+n

再贪心的考虑,如果需求最大的人满足要求,那么需求小的也能满足

因此判断即可

void solve(){
	cin>>n>>m;
	ll sum = 0;
	
	int minn = 0x3f3f3f3f;
	int maxn =  0;
	
	for(int i=1;i<=n;i++){
		 cin>>a[i];
		 sum+=a[i];
		 minn = min(a[i],minn);
		 maxn = max(maxn,a[i]);
	}
	
	sum+=maxn;
	sum-=minn;
	sum+=n;
	
	if(sum<= m) cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
	
}

C.

题意 :
给定 a [ ] a[] a[],对于初始的空数组 b [ ] b[] b[]

对于每一个 b [ i ] b[i] b[i]可以有任意次 + a [ i ] , − a [ i ] +a[i],-a[i] +a[i],a[i]操作,询问最少的操作次数

思路 :
感觉思维难度不如 A , B A,B A,B

首先我们如果傻乎乎的直接进行 + , − +,- +,操作,很明显的无厘头,而且每次操作都会影响后面以及前面的操作,不好判断何时最小

因此不妨考虑将某个位置 b [ i ] = 0 b[i] =0 b[i]=0,然后向两边扩展,这样子保证了一个数不需要操作,显然减少了一个不需要的次数

因此我们 n 2 n^2 n2的枚举即可

inline void solve(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];

	ll ans = 9e18;
	
	for(int i=1;i<=n;i++){
		ll b[n+1] = {0};
		
		b[i] = 0 ;
		
		ll cnt =0  ;
		
		for(int j = i-1;j>=1;j -- ){
			if(b[j]>=b[j+1]){
				ll  p = (b[j]-b[j+1])/a[j]+1;
				cnt  += p;
				b[j] -= p*a[j];
			}
		
		}
		
		if(cnt >= ans) continue;
		
		
		for(int j=i+1;j<=n;j++ ) {
			if(b[j]<=b[j-1]){
				ll  p =  (b[j-1]-b[j])/a[j]+1;
				cnt+=p;
				b[j]+= p*a[j];
			}
		}
		
		ans = min(cnt,ans);
	}	
	
	cout<<ans<<endl;
	
	
}

D.

题意 :
给定 a [ ] a[] a[],对于 s [ ] s[] s[]表示 a [ ] a[] a[]的前缀和

有对于 F ( l , r ) F(l,r) F(l,r)定义如下 :

  • r − l + 1 r-l+1 rl+1 N e e d Need Need s [ r ] − s [ l − 1 ] > 0 s[r]-s[l-1]>0 s[r]s[l1]>0
  • 0 0 0 N e e d Need Need s [ r ] − s [ l ] = 0 s[r]-s[l]=0 s[r]s[l]=0
  • − ( r − l + 1 ) -(r-l+1) (rl+1) N e e d Need Need s [ r ] − s [ l − 1 ] < 0 s[r]-s[l-1]<0 s[r]s[l1]<0

F ( l , r ) F(l,r) F(l,r)的和的最大值

思路 :
首先这题最先想到的是 动态规划

状态表示 :

f [ i ] f[i] f[i] i i i个数的最大价值和

状态转移 :

f [ i ] = f [ j ] + ( i − j ) f[i]=f[j]+(i-j) f[i]=f[j]+(ij)
f [ i ] = f [ j ] f[i]=f[j] f[i]=f[j]
f [ i ] = f [ j ] − ( i − j ) f[i]=f[j]-(i-j) f[i]=f[j](ij)

显然我们需要 n 2 n^2 n2的进行枚举


这里开始优化

首先需要知道的是
对于 f [ i ] = f [ j ] + ( i − j ) f[i]=f[j]+(i-j) f[i]=f[j]+(ij)
我们最终转移是 f [ i ] = m a x ( f [ j ] − j ) + i f[i]=max(f[j]-j)+i f[i]=max(f[j]j)+i

因此我们可以把 s [ i ] s[i] s[i]当作数组下标,然后查询所有小于 s [ i ] s[i] s[i]这个下标的 f [ j ] − j f[j]-j f[j]j的最大值

因为 s [ i ] s[i] s[i]范围过大,而且离散,所以我们考虑离散化,这样子我们就可以每次查询用来更新 f [ i ] f[i] f[i],然后将 下标为 s [ i ] s[i] s[i] 的值更新为 f [ i ] − i f[i]-i f[i]i

第二个和第三个式子同理

因为三个转移相互独立,因此需要建立三个线段树,但是操作相同,都是区间最大值,单点修改

// Problem: D. Optimal Partition
// Contest: Codeforces - Codeforces Round #783 (Div. 2)
// URL: https://codeforces.com/contest/1668/problem/D
// Memory Limit: 256 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <vector>
#include <map>
#include <cstring>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;
#define IOS  ios::sync_with_stdio(false);
#define CIT  cin.tie(0);
#define COT  cout.tie(0);

#define ll long long
#define x first
#define y second
#define pb push_back
#define endl '\n'
#define all(x) (x).begin(),x.end()
#define Fup(i,a,b) for(int i=a;i<=b;i++)
#define Fde(i,a,b) for(int i=a;i>=b;i--)

typedef priority_queue<int,vector<int>,greater<int>>  Pri_m;
typedef pair<int,int> pii;
typedef vector<int> VI;
const int N = 5e5+10,INF = 0x3f3f3f3f;

ll n;

map<ll,ll> mp;
ll a[N],s[N];
ll f[N];
vector<ll> q;

ll d;//三个线段树的下标

struct node{
	ll l,r,v;
}tr[3][N*4];

ll get(ll x){
	return lower_bound(all(q),x) - q.begin()+1;
}//获得离散化之后的坐标

void pushup(ll u){
	tr[d][u].v = max(tr[d][u<<1].v,tr[d][u<<1|1].v);
}

void build(ll u,ll l,ll r){
	if(l == r){
		tr[d][u] = {l,r,-INF};
		return;
	}
	
	tr[d][u] = {l,r};
	ll mid = (l+r)>>1;
	build(u<<1,l,mid);
	build(u<<1|1,mid+1,r);
	pushup(u);
}
/**区间查询最大值**/
ll query(ll u, ll l, ll r)
{
    if(tr[d][u].l >= l && tr[d][u].r <= r) 
        return tr[d][u].v ;
    
    ll v = -1e9 , minv = 1e9 ;
    ll mid = tr[d][u].l + tr[d][u].r >> 1 ;
    if(l <= mid) 
    {
        v = max(v,query(u << 1 , l ,r)) ; 
    }
    if(r > mid) 
    {
        v = max(v,query(u << 1 | 1 , l , r)) ;
    }
    
    return v ;
}
/**简单的单点修改**/
void modify(ll u, ll x, ll v)
{
    if(tr[d][u].l == x && tr[d][u].r == x) tr[d][u].v = max(v , tr[d][u].v) ;
    else
    {
        ll mid = tr[d][u].l + tr[d][u].r >> 1 ;
        if(x <= mid) modify(u << 1 , x , v);
        else modify(u << 1 | 1 , x ,v);
        pushup(u);
    }
}


void solve(){
	cin>>n;
	for(ll i=1;i<=n;i++) cin>>a[i],s[i] = s[i-1]+a[i];
	for(int i=0;i<=n;i++) f[i] = -INF;
	 
	
	f[0] = 0 ;
	q.clear();
	for(ll i=0;i<=n;i++) q.pb(s[i]);
	sort(all(q));
	q.erase(unique(all(q)),q.end());
	/**离散化**/
	
	/**初始化线段树**/
	for(ll i=0;i<=2;i++){
		d = i ;
		build(1,1,n+1);
	}
	
	/**初始化**/
	for(ll i=0;i<=2;i++){
		d = i;
		modify(1,get(s[0]),0);
	}
	
	for(ll i=1;i<=n;i++){
		ll w = get(s[i]) ;
            
            d = 0 ;
            //1~s[i]-1 中找最大
            //即所有小于s[i]的下标中找最大值
            f[i] = query(1 , 1 , w - 1) + i ;
            
            d = 1 ;
            //s[i] = s[j]
            f[i] = max(f[i] , query( 1 , w , w ) ) ;
            
            d = 2 ;
            //找比s[i]大的值
            f[i] = max(f[i] , query( 1 , w + 1 , n + 1) - i ) ;
            
            d = 0 ;
            //更新
            modify(1 , w , f[i] - i) ;
            
            d = 1 ;
            //更新
            modify(1 , w , f[i]) ;
            
            d = 2 ;
            modify(1 , w , f[i] + i) ;
	}
	
	cout<<f[n]<<endl;
	
}

int main(){
	IOS
	CIT
	COT
    int t;cin>>t;while(t--)
    solve();
    return 0 ;
}

总结 :
E. 肯定是要补的
比赛中虽然已经想到了 D D D需要用线段树优化,但是还是写不出来, s [ i ] s[i] s[i] 进行离散化当作下标这一步 还是不熟悉,虽然做 L u o g u Luogu Luogu省选经常碰到,但是总是想不到

显然这个 D D D也就洛谷蓝题难度,但是没写出来。做到 D D D的时候思维已经开始打转了,因为前面的 A A A,读错题 W A WA WA了两发,然后 B B B没开 L L LL LL WA了一发

主要是一开始看到 A A A,感觉是个Diao题,就直接从 C C C开了,导致做 A A A的时候心慌慌

每次做到 D D D的时候思维总是不集中,这是我该训练的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值