C:
由于确定了使用那个最小值,那么他只会单调往左推或者往右推,所以dp
前i个点使用j次的最小值即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=2*N,mod=1e9+7;
#define int long long
#define uLL unsigned long long
const long long inf=1e18;
typedef pair<int,int> PII;
typedef long long LL;
using Node=tuple<int,int,int,int>;
using node=tuple<int,int,int,int>;
int n,m,k;
int a[N];
void solve()
{
cin>>n>>m;
vector<vector<int>> f(n+10,vector<int>(m+10,inf));
for(int i=1;i<=n;i++) cin>>a[i];
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j]+a[i];
for(int k=i-1;k>=1;k--)
{
if(i-k<=j)
{
f[i][j]=min(f[i][j],f[k][j-(i-k)]+(i-k)*a[k]);
f[i][j]=min(f[i][j],f[k-1][j-(i-k)]+(i-k+1)*a[i]);
}else break;
}
}
}
int res=inf;
for(int i=0;i<=m;i++)
res=min(res,f[n][i]);
cout<<res<<"\n";
}
signed main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t=1;
cin>>t;
while(t--) solve();
return 0;
}
D:如果物品确定,那么Bob为了最小化b数组的总和,肯定最最大的k个b
而alice则会最小化这k个b的a
所以枚举分界点即可
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=2*N,mod=1e9+7;
#define int long long
#define uLL unsigned long long
const long long inf=1e18;
typedef pair<int,int> PII;
typedef long long LL;
using Node=tuple<int,int,int,int>;
using node=tuple<int,int,int,int>;
int n,m,k;
PII a[N];
int b[N],c[N];
void solve()
{
vector<PII> a;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++) cin>>c[i];
for(int i=1;i<=n;i++){
if(c[i]-b[i]>=0)
a.emplace_back(b[i],c[i]);
}
sort(a.begin(),a.end(),[&](const auto&p,const auto&q){
return p.second>q.second;
});
if(a.size()<=k){
cout<<0<<"\n";return ;
}
multiset<int> st;
//priority_queue<PII> q;
int now=0,res=0;
int s1=0,s2=0;
for(int i=0;i<k;i++)
{
st.insert(a[i].first);
s1+=a[i].first;
//s2+=a[i].second-a[i].first;
}
for(int i=k;i<a.size();i++){
s2+=a[i].second-a[i].first;
}
//for(auto [x,y]:a) cout<<x<<" "<<y<<"\n";
//cout<<s1<<" "<<s2<<"\n";
for(int i=k;i<a.size();i++)
{
res=max(res,s2-s1);
s2-=(a[i].second-a[i].first);
st.insert(a[i].first);
s1+=a[i].first;
s1-=*st.rbegin();
st.extract(*st.rbegin());
}
cout<<res<<"\n";
}
signed main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t=1;
cin>>t;
while(t--) solve();
return 0;
}
E:
问题转成二维扫点
有个结论:如果某个点到右端点不符合要求,那么只需要修改这个点就能保证前面的点都一定符合
要求,即每个右端点存在一个最近的不符合的点,只需要修改一次即可
且肯定要修改最大的左端点
所以用扫描线求出来最右的左端点即可
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=2*N,mod=1e9+7;
#define int long long
#define uLL unsigned long long
const long long inf=1e18;
typedef pair<int,int> PII;
typedef long long LL;
using node=tuple<int,int,int>;
int n,m,k;
int a[N];
class segment{
#define lson (u << 1)
#define rson (u << 1 | 1)
public:
int n;
vector<int> mn,add,pos;
segment(int _n) : n(_n) {
mn.resize(n*4+10,inf);
pos.resize(n*4+10);
add.resize(n*4+10);
build(1,1,n);
}
void pushup(int u){
mn[u]=min(mn[lson],mn[rson]);
if(mn[u]==mn[rson]) pos[u]=pos[rson];
else pos[u]=pos[lson];
}
void build(int u, int l, int r){
if (l == r)
{
add[u]=0;
mn[u]=0;
pos[u]=l;
return ;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(u);
}
void pushdown(int u)
{
if(add[u]){
add[lson]+=add[u];
add[rson]+=add[u];
mn[lson]+=add[u];
mn[rson]+=add[u];
}
add[u]=0;
}
void modify(int u, int l, int r, int L,int R, int val){
if (l>=L&&r<=R)
{
add[u]+=val;
mn[u]+=val;
return;
}
pushdown(u);
int mid = (l + r) >> 1;
if (L <= mid) modify(lson, l, mid, L,R, val);
if(R>mid) modify(rson, mid + 1, r,L ,R, val);
pushup(u);
}
//二分线段树
PII query(int u,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return {mn[u],pos[u]};
pushdown(u);
int mid=l+r>>1;
PII res={inf,0};
PII ans={inf,0};
if(L<=mid) res=query(lson,l,mid,L,R);
if(R>mid) ans=query(rson,mid+1,r,L,R);
res.first=min(res.first,ans.first);
if(res.first==ans.first) res.second=ans.second;
return res;
}
};
void solve()
{
cin>>n;
vector<int> pre(n+1),nxt(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
{
vector<int> last(n+1);
for(int i=1;i<=n;i++){
pre[i]=last[a[i]];
last[a[i]]=i;
}
}
{
vector<int> last(n+1,n+1);
for(int i=n;i>=1;i--){
nxt[i]=last[a[i]];
last[a[i]]=i;
}
}
vector<vector<PII>> pos(n+1);
for(int i=1;i<=n;i++)
{
pos[nxt[i]-1].push_back({i,1});
pos[i-1].push_back({i,0});
}
segment tr(n);
set<PII> st;
for(int i=n;i>=1;i--)
{
for(auto [x,type]:pos[i]){
if(type) tr.modify(1,1,n,pre[x]+1,x,1);
else tr.modify(1,1,n,pre[x]+1,x,-1);
}
auto [mn,mnpos]=tr.query(1,1,n,1,i);
if(mn==0){
// cout<<mnpos<<"\n";
st.insert({mnpos,i});
}
}
int ans=0;
int cur=n+1;
while(st.size()){
if(prev(st.end())->second>=cur)
st.erase(prev(st.end()));
else{
ans++;
cur=prev(st.end())->first;
st.erase(prev(st.end()));
}
}
cout<<ans<<"\n";
}
signed main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t=1;
cin>>t;
while(t--) solve();
return 0;
}