CF725F Family Photos

Description

https://www.luogu.com.cn/problem/CF725F

Solution

第一步都想错了,警戒!

考虑一个严格弱的问题:一堆照片只有一张的情况,有贪心策略两个人按 $a_i+b_i$ 轮流取。

从直接代数的角度上理解,如果 $a_i+b_i\ge a_j+b_j$,则有 $a_i-b_j\ge a_j-b_i$,对后手同理,先取 $i$ 一定不劣。

从更巧妙的构造上来说,把每张照片变成 $(\dfrac{a_i+b_i}{2},\dfrac{a_i+b_i}{2})$,而开始之前先把所有照片的代价加上 $\dfrac{a_i-b_i}{2}$,这样先后手取之后贡献不变,而贪心策略也就很直观了。

回到原来的问题。考虑 $a_{i,1}+b_{i,1}\le a_{i,2}+b_{i,2}$,第一张照片如果本来就要在第二张照片前面取,那么不用管栈的限制。否则:

  • 如果 $a_{i,1}\le b_{i,2}$ 且 $b_{i,1}\le a_{i,2}$,则两个人都不取这个。
  • 如果 $a_{i,1}\ge b_{i,2}$,则 A 取上面的。
  • 如果 $b_{i,1}\ge a_{i,2}$,则 B 取上面的。

排序即可。复杂度 $O(n\log n)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define maxn 200005
#define put() putchar('\n')
#define Tp template<typename T>
#define Ts template<typename T,typename... Ar>
using namespace std;
Tp void read(T &x){
int f=1;x=0;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
x*=f;
}
namespace Debug{
Tp void _debug(char* f,T t){cerr<<f<<'='<<t<<endl;}
Ts void _debug(char* f,T x,Ar... y){while(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);}
#define gdb(...) _debug((char*)#__VA_ARGS__,__VA_ARGS__)
}using namespace Debug;
#define fi first
#define se second
#define mk make_pair
const int mod=1e9+7;
int power(int x,int y=mod-2) {
int sum=1;
while (y) {
if (y&1) sum=sum*x%mod;
x=x*x%mod;y>>=1;
}
return sum;
}
int ans,a[maxn],b[maxn],c[maxn],n,cnt;
signed main(void){
int i;
read(n);
for (i=1;i<=n;i++) {
read(a[i]);read(b[i]);read(a[i+n]);read(b[i+n]);
if (a[i]+b[i]>=a[i+n]+b[i+n]) {
c[++cnt]=a[i]+b[i];
c[++cnt]=a[i+n]+b[i+n];
ans+=a[i]-b[i]+a[i+n]-b[i+n];
}
else if (a[i]>=b[i+n]) ans+=2*(a[i]-b[i+n]);
else if (b[i]>=a[i+n]) ans+=2*(a[i+n]-b[i]);
}
sort(c+1,c+1+cnt);
reverse(c+1,c+1+cnt);
for (i=1;i<=cnt;i++)
if (i&1) ans+=c[i];
else ans-=c[i];
printf("%lld\n",ans/2);
return 0;
}
//i=begin && g++ $i.cpp -o $i -std=c++14 && ./$i

总结

这种先手后手问题的策略大概率是严格对称优雅简洁的。

对称可以理解为先手取完以后后手就变成了先手,不会出现什么“按差值排序一个取最大一个取最小”,因为也要考虑两者相互干扰的情况。

优雅在于不需要管 $\ge$ 还是 $>$,细节不是重点。

贪心策略还是要多积累。