二分枚举即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+107;
int n,m;
int l[N],r[N];
int read()
{
int f=1,s=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return f*s;
}
bool check(int ans)
{
if(r[1]+ans>=l[n]&&r[m]-ans<=l[1]) return 1;
int x=upper_bound(l+1,l+1+n,r[1]-ans)-(l+1);
if(l[x]>r[1]+ans) return 0;
for(int i=1;i<=m;i++)
{
x++;
while(x<=n&&l[x]<r[i]-ans) x++;
if(l[x]>r[i]+ans) return 0;
if(x>n) return 0;
}
return 1;
}
signed main()
{
freopen("gloves.in","r",stdin);
freopen("gloves.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++) l[i]=read();
for(int i=1;i<=m;i++) r[i]=read();
if(m>n) swap(n,m),swap(l,r);
sort(l+1,l+1+n),sort(r+1,r+1+m);
int li=0,ri=1e9+10,ans=0;
while(li<=ri)
{
int mid=(li+ri)>>1;
if(check(mid))
{
ri=mid-1;
ans=mid;
}
else li=mid+1;
}
printf("%lld",ans);
}
DP,直接设 \(f[i][j][k][0/1/2]\) 表示目前填了 \(i\) 个 \(0\) ,\(j\) 个 \(1\) ,\(k\) 个 \(2\) ,当前为 \(0/1/2\) ,转移也好说,以 \(0\) 举例。
\(f[i][j][k][0]=min(f[i-1][j][k][1],f[i-1][j][k][2])+abs(i+j+k-c[0][i])\) 。
其中 \(c[i][j]\) 表示 \(0/1/2\) 的第 \(j\) 个数。
由于这个移动在DP中是单向的,而不是正常模拟的双向,所以最后答案要除 \(2\) 。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=300;
char s[N];
int len;
int a[N],f[N][N][N][3];
int c[3][N],num[N];
int read()
{
int f=1,s=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return f*s;
}
int main()
{
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
scanf(" %s",s+1);
len=strlen(s+1);
for(int i=1;i<=len;i++)
{
a[i]=s[i]-'0';
num[a[i]]++;
c[a[i]][num[a[i]]]=i;
}
for(int i=0;i<=2;i++) if(num[i]>(len+1)/2) return printf("-1"),0;
memset(f,0x3f,sizeof f);
f[0][0][0][0]=f[0][0][0][1]=f[0][0][0][2]=0;
for(int i=0;i<=num[0];i++)
{
for(int j=0;j<=num[1];j++)
{
for(int k=0;k<=num[2];k++)
{
int sum=i+j+k;
if(i) f[i][j][k][0]=min(f[i-1][j][k][1],f[i-1][j][k][2])+abs(sum-c[0][i]);
if(j) f[i][j][k][1]=min(f[i][j-1][k][0],f[i][j-1][k][2])+abs(sum-c[1][j]);
if(k) f[i][j][k][2]=min(f[i][j][k-1][0],f[i][j][k-1][1])+abs(sum-c[2][k]);
}
}
}
int i=num[0],j=num[1],k=num[2];
int ans=min({f[i][j][k][0],f[i][j][k][1],f[i][j][k][2]});
printf("%d",ans/2);
}
由于 \(k\) 比较小,我们直接开线段树维护,那我们考虑如何去合并区间来得到答案,跟山海经那题一样的思路,首先根节点的答案肯定可以由左右两颗子树直接转移,问题主要出在两个区间合并中间的那段区间也会产生答案。
我们维护 \(k\) 的一个前驱和后继,合并肯定是根节点左儿子的后继和右儿子的前驱来合并,至于合并的方法,我们可以考虑莫队那样的移动用双指针来进行操作。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+107;
int n,k,m,a[N];
int ans[N],len[N];
#define lson (rt<<1)
#define rson (rt<<1|1)
struct lmy
{
int pre[31],nxt[31];
int ans;
int l,r;
}tr[N<<2];
struct qx
{
int pos,val;
}tmp[70];
void clear(){for(int i=1;i<=k;i++) tmp[i].pos=tmp[i].val=0;}
bool comp(qx a,qx b){return a.pos<b.pos;}
int read()
{
int f=1,s=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return f*s;
}
void pushup(int rt)
{
for(int i=1;i<=k;i++) tr[rt].pre[i]=tr[rt].nxt[i]=0;
tr[rt].ans=1e18;
tr[rt].ans=min(tr[lson].ans,tr[rson].ans);
for(int i=1;i<=k;i++) tr[rt].pre[i]=tr[lson].pre[i];
for(int i=1;i<=k;i++) if(!tr[rt].pre[i]) tr[rt].pre[i]=tr[rson].pre[i];
for(int i=1;i<=k;i++) tr[rt].nxt[i]=tr[rson].nxt[i];
for(int i=1;i<=k;i++) if(!tr[rt].nxt[i]) tr[rt].nxt[i]=tr[lson].nxt[i];
clear();int len=0;
for(int i=1;i<=k;i++)
{
if(tr[rson].pre[i]!=0) tmp[++len]={tr[rson].pre[i],i};
if(tr[lson].nxt[i]!=0) tmp[++len]={tr[lson].nxt[i],i};
}
sort(tmp+1,tmp+1+len,comp);
int j=0,sum=0,cnt[50]={},ans=1e18;
for(int i=1;i<=len;i++)
{
while(j<len&&sum<k)
{
j++;
if(cnt[tmp[j].val]<=0) sum++;
cnt[tmp[j].val]++;
}
if(sum<k) break;
cnt[tmp[i].val]--;
if(cnt[tmp[i].val]<=0) sum--;
ans=min(ans,tmp[j].pos-tmp[i].pos+1);
}
tr[rt].ans=min(tr[rt].ans,ans);
}
void build(int rt,int l,int r)
{
tr[rt].l=l,tr[rt].r=r;
tr[rt].ans=1e18;
if(l==r)
{
tr[rt].pre[a[l]]=tr[rt].nxt[a[l]]=l;
return ;
}
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(rt);
}
void update(int rt,int pos,int val)
{
if(tr[rt].l==tr[rt].r)
{
tr[rt].pre[a[tr[rt].l]]=tr[rt].nxt[a[tr[rt].l]]=0;
a[tr[rt].l]=val;
tr[rt].pre[val]=tr[rt].nxt[val]=tr[rt].l;
return ;
}
int mid=(tr[rt].l+tr[rt].r)>>1;
if(pos<=mid) update(lson,pos,val);
if(pos>mid) update(rson,pos,val);
pushup(rt);
}
signed main()
{
freopen("truth.in","r",stdin);
freopen("truth.out","w",stdout);
n=read(),k=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++)
{
int op=read();
if(op==1)
{
int pos=read(),val=read();
update(1,pos,val);
}
else
{
if(tr[1].ans==1e18) printf("-1\n");
else printf("%lld\n",tr[1].ans);
}
}
}