分糖果
有点像国王游戏,考虑贪心
考虑交换 ( a , b ) (a,b) (a,b) 和 ( c , d ) (c,d) (c,d) 会有什么区别,令 c i − 2 = p r e , ∑ i = 1 i − 2 a i = s u m c_{i-2}=pre,\sum_{i=1}^{i-2}a_i=sum ci−2=pre,∑i=1i−2ai=sum
( a , b ) (a,b) (a,b) 在 ( c , d ) (c,d) (c,d) 前:
m a x ( m a x ( p r e , s u m + a ) + b , s u m + a + c ) + d = m a x ( p r e + b + d , s u m + a + b + d , s u m + a + c + d ) max(max(pre,sum+a)+b,sum+a+c)+d=max(pre+b+d,sum+a+b+d,sum+a+c+d) max(max(pre,sum+a)+b,sum+a+c)+d=max(pre+b+d,sum+a+b+d,sum+a+c+d)
( a , b ) (a,b) (a,b) 在 ( c , d ) (c,d) (c,d) 后
m a x ( m a x ( p r e , s u m + c ) + d , s u m + a + c ) + b = m a x ( p r e + b + d , s u m + b + c + d , s u m + a + b + c ) max(max(pre,sum+c)+d,sum+a+c)+b=max(pre+b+d,sum+b+c+d,sum+a+b+c) max(max(pre,sum+c)+d,sum+a+c)+b=max(pre+b+d,sum+b+c+d,sum+a+b+c)
p r e pre pre 的那一项有点烦,不妨先去掉再看,若 ( a , b ) (a,b) (a,b) 先更优
m a x ( a + b + d , a + c + d ) ≤ m a x ( b + c + d , a + b + c ) max(a+b+d,a+c+d)\le max(b+c+d,a+b+c) max(a+b+d,a+c+d)≤max(b+c+d,a+b+c)
全部减去 a + b + c + d a+b+c+d a+b+c+d
m a x ( − c , − b ) ≤ m a x ( − a , − d ) max(-c,-b)\le max(-a,-d) max(−c,−b)≤max(−a,−d)
m i n ( c , b ) ≥ m i n ( a , d ) min(c,b)\ge min(a,d) min(c,b)≥min(a,d)
于是我们的贪心策略就是如果两个数 m i n ( a , d ) ≤ m i n ( b , c ) min(a,d)\le min(b,c) min(a,d)≤min(b,c)就交换
现在来考虑 p r e pre pre 的影响
无论 p r e pre pre 取不取 m a x max max,交换后的总是最优的
upt:与洛谷上的 皇后游戏 一样
在题解中发现了直接排序的 h a c k hack hack 数据
具体可以看第一篇题解 传送门
然后有一种巧妙的排序方法
看到取 m i n min min 应该想到分类讨论:
a i ≤ b i , a j ≤ b j a_i\le b_i,a_j\le b_j ai≤bi,aj≤bj:按 a i ≤ a j a_i\le a_j ai≤aj 排序
a i > b i , a j > b j a_i>b_i,a_j>b_j ai>bi,aj>bj:按 b i ≥ b j b_i\ge b_j bi≥bj 排序
当 a i ≤ b i , a j > b j a_i\le b_i,a_j> b_j ai≤bi,aj>bj 把 i i i 放前面
发现在第一组,有 a i ≤ a j , a i ≤ b i a_i\le a_j,a_i\le b_i ai≤aj,ai≤bi,也就是 a i ≤ m i n ( a j , b i ) a_i\le min(a_j,b_i) ai≤min(aj,bi)
发现在第二组,有 b j ≤ b i b_j\le b_i bj≤bi, b j < a j b_j
发现交界处,有 a i ≤ b i , a j > b j a_i\le b_i,a_j> b_j ai≤bi,aj>bj,所以一定有 m i n ( a i , b j ) ≤ m i n ( a j , b i ) min(a_i,b_j)\le min(a_j,b_i) min(ai,bj)≤min(aj,bi)
总之,这一类贪心题就是基于交换的思想,看交换后更不更优
#include
#define N 50050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int T, n, a[N], b[N], id[N], sgn[N];
typedef long long ll;
void calc(){
ll mx = 0, sum = 0;
for(int i = 1; i <= n; i++){
int now = id[i];
sum += (ll)a[now]; mx = max(mx, sum);
mx += (ll)b[now];
} cout << mx << '\n';
}
bool cmp(int i, int j){
if(sgn[i] != sgn[j]) return sgn[i] < sgn[j];
if(sgn[i] <= 0) return a[i] < a[j];
return b[i] > b[j];
}
int main(){
T = read();
while(T--){
n = read();
for(int i = 1; i <= n; i++){
a[i] = read(), b[i] = read(), id[i] = i;
if(a[i] < b[i]) sgn[i] = -1;
if(a[i] == b[i]) sgn[i] = 0;
if(a[i] > b[i]) sgn[i] = 1;
} sort(id + 1, id + n + 1, cmp); calc();
} return 0;
}
异或
比较妙的一道二合一,需要用到数位 d p dp dp 的思想,以及计数问题的一些思想,还有期望概率的一些知识
先考虑随机的情况,显然就是总的异或和 / n 2 /n^2 /n2
按位算贡献,令 p i p_i pi 为第 i i i 位为 1 的概率
a n s = 2 ∗ ∑ i = 0 l o g n 2 i ∗ p i ( 1 − p i ) ans=2*\sum_{i=0}^{log_n}2^i * p_i(1-p_i) ans=2∗i=0∑logn2i∗pi(1−pi)
考虑如何算 p i p_i pi
[ 0 , 2 i ) [0,2^i) [0,2i) 为 0
[ 2 i , 2 i + 1 ) [2^i,2^{i+1}) [2i,2i+1) 为 1
[ 2 i + 1 , 2 i + 1 + 2 i ) [2^{i+1},2^{i+1}+2^i) [2i+1,2i+1+2i) 为 0
[ 2 i + 1 + 2 i , 2 ∗ 2 i + 1 ) [2^{i+1}+2^i,2*2^{i+1}) [2i+1+2i,2∗2i+1) 为 1
[ k ∗ 2 i + 1 , k ∗ 2 i + 1 ) [k*2^{i+1},k*2^{i+1}) [k∗2i+1,k∗2i+1) 为 1
p i = ⌊ n 2 i + 1 ⌋ ∗ 2 i + m a x ( n % 2 i + 1 , 0 ) n p_i=\frac{\left \lfloor \frac{n}{2^{i+1}} \right \rfloor * 2 ^ i+max(n \% 2^{i+1}, 0)}{n} pi=n⌊2i+1n⌋∗2i+max(n%2i+1,0)
考虑第二种怎么做
填满的限制特别讨厌,先强制 x x x 的第一位为 1,然后再强制填 0
以下令 n n n 为 n − 1 n-1 n−1不妨设 n n n 有 l l l 为,当 x x x 的 l l l 位为 1 时, f ( x ) f(x) f(x) 的 l l l 贪心填 0,而且后面可以随便填
也就是说,对于 [ 2 l , n ] [2^l,n] [2l,n] 这 ( n − 2 l + 1 ) (n-2^l+1) (n−2l+1) 个数,它们每一个都有 2 l + 1 − 1 2^{l+1}-1 2l+1−1 的贡献
好,现在 x x x 没有限制, f ( x ) f(x) f(x) 有限制
同样按位考虑
为了使我们的计数方案不重不漏,我们在考虑第 k k k 位时,强制 k k k 之前的 f ( x ) f(x) f(x) 顶着选
然后统计在 f ( x ) f(x) f(x) 在第 k k k 位不满的贡献
讨论:
当前位为 0
x x x 可以填 0 或 1, f ( x ) f(x) f(x) 只能选 0 0 0
当前位有贡献当且仅当 x x x 填 1
方案数为 2 k ∗ 2 n u m 2 \frac{2^k*2^{num}}{2} 22k∗2num, n u m num num 为 k 位之前的 0 0 0 的个数,当前强制填1
当前位为 1
x x x 填 0, f ( x ) f(x) f(x) 填 1,有 2 k 2^k 2k 的贡献,强制填0,方案数为 2 k ∗ 2 n u m 2 \frac{2^k*2^{num}}{2} 22k∗2num
x x x 填 1, f ( x ) f(x) f(x) 填 0,后面都可以补成 1,贡献为 2 k + 1 − 1 2^{k+1}-1 2k+1−1,当前强制填 1,方案数 2 k ∗ 2 n u m 2 \frac{2^k*2^{num}}{2} 22k∗2num
来解释一下为什么要用 0 的个数来算
先看一下我们到底是怎么算的
枚举 f ( x ) f(x) f(x) 第一个没有顶满的位置,算它的可以作为多少个 x x x 的 f ( x ) f(x) f(x) 以及它对答案的贡献
它的贡献可以看做 k k k 位之前顶满的贡献 + k k k 位即之后全部补满的贡献
而对于当前 k k k 位没有顶满的 f ( x ) f(x) f(x),它能作为哪些 x x x 的 f ( x ) f(x) f(x) 呢,就是之前 0 的那些位置随便填,1 的位置只能填 0 的 x x x,因为如果本来是 1 的位置填了 0,它的 f ( x ) f(x) f(x) 应该在它那一位就算了
最近已经做了好多到这种题:
枚举第一个没有顶满的位置,分前后讨论起来就会特别简单
然后就是计数的一些套路及化简方法
例如强制 x x x 的首位为 0,后面就可以随便填
强制 f ( x ) f(x) f(x) 顶满,枚举第一个不顶满的位置…
#include
using namespace std;
typedef long long ll;
ll n; double p;
double solve(ll x){
if(!x) return 0.0;
double ret = 0.0;
ll v = 1ll, delta, num;
while(v <= x) v <<= 1;
delta = v - 1ll; v >>= 1;
ret += (double)delta * (x - v + 1);
ret += (double)v * v;
num = v, delta >>= 1;
while(v != 1){
v >>= 1, delta >>= 1;
if(x & v){
ret += (double)num * v;
ret += (double)(num >> 1) * delta;
num >>= 1;
} else ret += (double)(num >> 1) * v;
} return ret / (double)n;
}
int main(){
scanf("%lld%lf", &n, &p);
double ans1 = 0;
for(ll i = 0; (1ll << i) <= n; i++){
ll k = n / (1ll << i + 1);
k = k * (1ll << i) + max(n % (1ll << i + 1) - (1ll << i), 0ll);
double pi = k / (n * 1.0);
ans1 += 2ll * pi * (1 - pi) * (1ll << i);
}
double ans2 = solve(n - 1);
double ans = ans1 * (1.0 - p) + ans2 * p;
int b = 0;
while(ans >= 10.0) ans /= 10.0, ++b;
while(ans > 0.0 && ans < 1.0) ans *= 10.0, --b;
printf("%.5f %d", ans, b);
return 0;
}