前言
A.
题意 :
给你$n,m$ 询问从$1,1$往四个方向走,不能连续选择一条直线上的方向(以为是一个方向去世,问最少步数
思路 :
显然对于$(1,1)->(x,x)$这种一定是$(x-1)*2$的,然后我们补上一下后面即可
画个图会发现,偶数的时候需要多走$d+1$,奇数多走$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$个人$m$个凳子,$i$人有$a[i]$的要求,要求他做的位置两边都要有$a[i]$个空凳子
询问是否可行
思路 :
考虑成环,顺序排列,因此总需要
$(\sum_{i=1}^na[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[]$,对于初始的空数组$b[]$
对于每一个$b[i]$可以有任意次$+a[i],-a[i]$操作,询问最少的操作次数
思路 :
感觉思维难度不如$A,B$
首先我们如果傻乎乎的直接进行$+,-$操作,很明显的无厘头,而且每次操作都会影响后面以及前面的操作,不好判断何时最小
因此不妨考虑将某个位置$b[i] =0$,然后向两边扩展,这样子保证了一个数不需要操作,显然减少了一个不需要的次数
因此我们$n^2$的枚举即可
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.
此题借鉴 @ZZXzzx0_0
题意 :
给定$a[]$,对于$s[]$表示$a[]$的前缀和
有对于$F(l,r)$定义如下 :
-
$r-l+1$ $Need$ $s[r]-s[l-1]>0$
-
$0$ $Need$ $s[r]-s[l]=0$
-
$-(r-l+1)$ $Need$ $s[r]-s[l-1]<0$
求$F(l,r)$的和的最大值
思路 :
首先这题最先想到的是 动态规划
状态表示 :
$f[i]$ 前$i$个数的最大价值和
状态转移 :
$f[i]=f[j]+(i-j)$
$f[i]=f[j]$
$f[i]=f[j]-(i-j)$
显然我们需要$n^2$的进行枚举
这里开始优化
首先需要知道的是
对于$f[i]=f[j]+(i-j)$
我们最终转移是$f[i]=max(f[j]-j)+i$
因此我们可以把 $s[i]$当作数组下标,然后查询所有小于$s[i]$这个下标的$f[j]-j$的最大值
因为$s[i]$范围过大,而且离散,所以我们考虑离散化,这样子我们就可以每次查询用来更新$f[i]$,然后将 下标为$s[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$需要用线段树优化,但是还是写不出来,将$s[i]$ 进行离散化当作下标这一步 还是不熟悉,虽然做$Luogu$省选经常碰到,但是总是想不到
显然这个$D$也就洛谷蓝题难度,但是没写出来。做到$D$的时候思维已经开始打转了,因为前面的$A$,读错题$WA$了两发,然后$B$没开$LL$ WA了一发
主要是一开始看到$A$,感觉是个Diao题,就直接从$C$开了,导致做$A$的时候心慌慌
每次做到$D$的时候思维总是不集中,这是我该训练的