Educational Codeforces Round 76 (Rated for Div. 2) E. The Contest(dp+線段樹)
題目鏈接
題意:
給定3個人互不相同的多個數字,可以把數字移動給別人,問最少移動幾次后可以使第一個人的數字為1~m1,第二個人m1~m2,第三個人m2~n(可以沒有數字)
題解:
設c[1][i]為第一個人m1為i時需要移動的次數,c[3][i]為m2為i是第三個人需要操作的次數,當其他兩個人數字合法時,第二個人的數字也會合法.枚舉第一個人的每個i,查詢m2為(i+1~n+1)的最小操作次數,ans = min{c[1][i]+min(c[3]k)} 查詢操作可用線段樹維護
代碼:
#include <bits/stdc++.h>
using namespace std;
const int N = 250000;
int t[4*N],a[5][N],b[5][N],c[5][N];
void build(int x,int l,int r)
{
if (l == r)
{
t[x] = c[3][l];
return;
}
int mid = (l + r) >>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
t[x] = min(t[x<<1] , t[x<<1|1]);
}
int query(int x,int l,int r,int ll,int rr)
{
if (ll <= l && r <= rr) return t[x];
int mid = (l + r) >> 1;
int ans = 1<<30;
if (ll <= mid) ans = min(ans,query(x<<1,l,mid,ll,rr));
if (rr > mid ) ans = min(ans,query(x<<1|1,mid+1,r,ll,rr));
return ans;
}
int main()
{
int k1,k2,k3;
cin >> k1 >> k2 >> k3;
int n = k1+k2+k3;
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
for (int i = 1; i <= k1; i++)
{
cin >> a[1][i];
b[1][a[1][i]] = 1;
}
for (int i = 1; i <= k2; i++) cin >> a[2][i];
for (int i = 1; i <= k3; i++)
{
cin >> a[3][i];
b[3][a[3][i]] = 1;
}
c[1][0] = k1;
for (int i = 1; i <= n; i++)
{
b[1][i] = b[1][i-1]+b[1][i]; //記錄第一個人前i個數中擁有幾個
c[1][i] = i - b[1][i] + k1 - b[1][i];//i-b[1][i]為需要移動到第一個人的操作數,k1-b[1][i]為第一個人把數移動出去的操作數
}
c[3][n+1] = k3;
for (int i = n; i; i--)
{
b[3][i] = b[3][i+1] + b[3][i];
c[3][i] = n-i+1-b[3][i] +k3 - b[3][i]-(k1-b[1][i-1]); //如果是第一個人可以移動到第三個人的數會重復需要減掉
}
for (int i = 1; i < n; i++)
c[1][i] -= k3-b[3][i+1];//第三個人可以移動到第一個人的數
build(1,0,n+1);
int ans = c[1][n];
for (int i = 0; i < n; i++)
{
ans = min(ans, c[1][i] + query(1,0,n,i+1,n+1));
}
cout << ans << endl;
}