AtCoder Regular Contest 197 (Div. 2) Tutorial for B~D
B - Minimum Cost Sort
考虑有什么性质。交换的位置越靠右,花费就越大,这启示我们要尽量避免在右边交换。
如何避免呢?考虑贪心,先把 \(n\) 放到最右边,然后是 \(n-1\)……让大数先归位,减少了之后在右边的交换,就能减少花费。
使用树状数组标记排列中有哪些位置上的数被移走了,使用等差数列求和即可统计答案。
// B - Minimum Cost Sort
#include <cstdio>
#include <iostream>
#define ll long long
#define rep(i, s, t) for(int i=s; i<=t; ++i)
#define debug(x) cerr<<#x<<":"<<x<<endl;
const int N=200010;
using namespace std;
char buf[1<<21], *p1=buf, *p2=buf;
#define gc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read()
{
int x=0, f=1; char c=gc();
while(c<'0' || c>'9') c=='-' && (f=-1), c=gc();
while('0'<=c && c<='9') x=(x<<3)+(x<<1)+c-'0', c=gc();
return x*f;
}
int n, a[N], pos[N]; ll res;
int c[N];
inline void upd(int x) {for(; x<=n; x+=x&-x) ++c[x];}
inline int sum(int x)
{
int res=0; for(; x; x-=x&-x) res+=c[x];
return res;
}
inline ll Range(int l, int r) {return (r+l)*(r-l+1ll)>>1;}
int main()
{
#ifdef Jerrywang
freopen("E:/OI/in.txt", "r", stdin);
#endif
n=read();
rep(i, 1, n) a[i]=read(), pos[a[i]]=i;
for(int x=n; x; x--)
{
int i=pos[x], p=i-sum(i);
res+=Range(p, x-1); upd(i);
}
printf("%lld", res);
return 0;
}
C - Cost to Flip
很有意思的题目!给一个全程无需树状数组的解法。
首先有一个很简单的思路:设两个序列 \(v_1=\{c_i|a_i=1\land b_i=0\},v_2=\{c_i|a_i=0\land b_i=1\}\)。把 \(v_1\) 从大到小排序,\(v_2\) 从小到大排序,依次翻转 \(v_1\),然后翻转 \(v_2\)。
不难发现这样是错的。假设 \(c_x\) 非常大,然而 \(a_x=1\land a_y=1\),这也就意味着按上述操作,\(c_x\) 这么一个非常大的累赘会在每一步中贡献代价,不优。
再设序列 \(v=\{c_i|a_i=1\land b_i=1\}\),从大到小排序。考虑增量法求答案。也就是说,考虑 \(v_1\sim v_i\) 这些 \(1\) 都是先删后加回,其余的 \(1\) 保持不变。这会对答案有如下图的贡献:
1 2 3 4 5 1 2 3 4 5
* * * * * * * * * *
* * * * * * * *
* * * <- 把 2 删掉
* * * ? * *
* * ? *
=>
* * <- 再把 2 加回来
* * * * * *
上图中用 * 表示 \(1\),假设位置 \(2\) 上这个 \(1\) 改成 \(0\) 后又改成 \(1\),多出了箭头所指的两步。但是,? 的两处原来是 \(1\),现在 \(1\) 缺席了。
在什么时间删,又在什么时间加回来呢?考虑结合上原来的思路,即可二分找到位置。注意内置的二分函数的使用。细节很多。
// C - Cost to Flip
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#define ll long long
#define rep(i, s, t) for(int i=s; i<=t; ++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
const int N=200010;
using namespace std;
char buf[1<<21], *p1=buf, *p2=buf;
#define gc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read()
{
int x=0, f=1; char c=gc();
while(c<'0' || c>'9') c=='-' && (f=-1), c=gc();
while('0'<=c && c<='9') x=(x<<3)+(x<<1)+c-'0', c=gc();
return x*f;
}
int n, a[N], b[N], c[N]; ll s, tmp1[N], tmp2[N], cur, ans, delta;
int v1[N], n1, v2[N], n2, v[N], m;
int main()
{
#ifdef Jerrywang
freopen("E:/OI/in.txt", "r", stdin);
#endif
n=read();
rep(i, 1, n) a[i]=read();
rep(i, 1, n) b[i]=read();
rep(i, 1, n) c[i]=read();
rep(i, 1, n)
{
if(a[i]) s+=c[i];
if(a[i] && !b[i]) v1[++n1]=c[i]; // 删除多余的1
if(!a[i] && b[i]) v2[++n2]=c[i]; // 添加没有的1
if(a[i] && b[i]) v[++m]=c[i];
}
sort(v1+1, v1+n1+1, greater<int>());
sort(v2+1, v2+n2+1);
sort(v+1, v+m+1, greater<int>());
tmp1[0]=s;
rep(i, 1, n1) s-=v1[i], tmp1[i]=s, ans+=s;
tmp2[0]=s;
rep(i, 1, n2) s+=v2[i], tmp2[i]=s, ans+=s;
cur=ans;
rep(i, 1, m)
{
int p1=lower_bound(v1+1, v1+n1+1, v[i], greater<int>())-v1-1;
cur+=tmp1[p1]-delta-v[i]-(ll)(n1-p1)*v[i];
int p2=upper_bound(v2+1, v2+n2+1, v[i])-v2-1;
delta+=v[i];
cur+=tmp2[p2]-delta+v[i]-(ll)p2*v[i];
ans=min(ans, cur);
}
printf("%lld", ans);
return 0;
}

浙公网安备 33010602011771号