*C.Painting Machines
不好求得分为恰好为 的排列个数,则考虑转化为求解得分 的排列个数 , 。
很容易得到得分
的有序排列个数
:
,而其它数差分后的值
。因为操作次数是固定的,且
,所以
的个数也是固定的,设分别为
,则
容斥求出所有得分 ( )的排列个数 。
*D.Go Home
每次贪心选期望最大显然倒序做,DP的复杂度做不了,观察出以下性质:
考虑只剩下两个最远处的位置 没到的状态:
-
如果它们在车的同侧,直接把车开过去即可。
-
否则假设 ,必然存在 ,所以在 到达前, 一定会一直帮着 ,使得 尽量小,从而 尽量小。
可以倒序构造,维护 ,初始化 ,当 在车的一侧时终止,否则若 ,在到达 前, 的意志完全代表 的意志,于是 ,然后 , 的情况同理可得。
具体可以看一下代码
code from sigongzi
#include <iostream>
#include <cstdio>
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
int N;
int64 S,X[MAXN],P[MAXN],ans;
void Solve() {
scanf("%d%lld",&N,&S);
for(int i = 1 ; i <= N ; ++i) {
scanf("%lld%lld",&X[i],&P[i]);
}
int L = 1,R = N;
int dir = 0;
while(1) {
if(X[L] >= S) {ans += X[R] - S;break;}
if(X[R] <= S) {ans += S - X[L];break;}
if(P[L] >= P[R]) {
if(dir != 1) {dir = 1;ans += X[R] - X[L];}
P[L] += P[R];R--;
}
else {
if(dir != 2) {dir = 2;ans += X[R] - X[L];}
P[R] += P[L];L++;
}
}
printf("%lld\n",ans);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}
*E.Inversions
统计合法全排列个数的方法:
设 表示 的位置个数,考虑从大到小选数,方案数即
枚举逆序对,强制 ,分类讨论:
- ,强制 ,贡献为合法排列数
- ,贡献为总排列数-(强制 后的合法排列数/2)
考虑按权值从大往小做,维护以位置为下标的BIT
从
的地方切开分成若干段,每段中强制
时,
一段相当于乘上了
,注意判断区间存在
的情况,
*F.01 on Tree
一种比较经典的贪心方式
设点 子树内 的个数为 , 的个数为 ,显然先走 最大的点
单点的情况是固定的,可以用大根堆存储,每次弹出堆顶点 :
- 若 ( 为根),则删去 后继续弹堆
- 否则 还没有走过,根据贪心走到 后一定会立刻进入 ,贡献为 ,那么 就是一体的了,并查集缩成一个点后重新压入堆