【Atcoder】AGC023 C-F简要题解

*C.Painting Machines

不好求得分为恰好为 i i i的排列个数,则考虑转化为求解得分 ≥ i \geq i i的排列个数 g [ i ] g[i] g[i] a n s = ∑ i = 1 n g [ i ] ans=\sum \limits_{i=1}^ng[i] ans=i=1ng[i]

很容易得到得分 ≤ i \leq i i的有序排列个数 w [ i ] w[i] w[i]
1 , n − 1 ∈ P 1,n-1\in P 1,n1P,而其它数差分后的值 ∈ { 1 , 2 } \in \{1,2\} {1,2}。因为操作次数是固定的,且 ∑ 1 + ∑ 2 = n − 2 \sum 1+\sum 2=n-2 1+2=n2,所以 1 , 2 1,2 1,2的个数也是固定的,设分别为 c n t 0 , c n t 1 cnt_0,cnt_1 cnt0,cnt1,则 w [ i ] = ( i − 1 ) ! c n t 0 ! c n t 1 ! w[i]=\dfrac{(i-1)!}{cnt_0!cnt_1!} w[i]=cnt0!cnt1!(i1)!

容斥求出所有得分 > i >i >i( ≥ i + 1 \geq i+1 i+1)的排列个数 ( ( n − 1 i − 1 ) − w [ i ] ) i ! ( n − i ) ! ({n-1\choose i-1}-w[i])i!(n-i)! ((i1n1)w[i])i!(ni)!


*D.Go Home

每次贪心选期望最大显然倒序做,DP的复杂度做不了,观察出以下性质:

考虑只剩下两个最远处的位置 l , r l,r l,r没到的状态:

  • 如果它们在车的同侧,直接把车开过去即可。

  • 否则假设 P a > P b P_a>P_b Pa>Pb,必然存在 T b = T a + d i s ( X a , X b ) T_b=T_a+dis(X_a,X_b) Tb=Ta+dis(Xa,Xb),所以在 a a a到达前, b b b一定会一直帮着 a a a,使得 T a T_a Ta尽量小,从而 T b T_b Tb尽量小。

可以倒序构造,维护 L , R L,R L,R,初始化 L = 1 , R = n L=1,R=n L=1,R=n,当 L , R L,R L,R在车的一侧时终止,否则若 P L ≥ P R P_L\geq P_R PLPR,在到达 L L L前, L L L的意志完全代表 R R R的意志,于是 P L + = P R P_L+=P_R PL+=PR,然后 R − − R-- R P L &lt; P R P_L&lt;P_R PL<PR的情况同理可得。

具体可以看一下代码
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

统计合法全排列个数的方法:

c n t i cnt_i cnti表示 A ≥ i A\geq i Ai的位置个数,考虑从大到小选数,方案数即 ∏ i = 1 n ( c n t i − ( n − i ) ) \prod\limits_{i=1}^n (cnt_i-(n-i)) i=1n(cnti(ni))

枚举逆序对,强制 i &lt; j , P i &gt; P j i&lt;j,P_i&gt;P_j i<j,Pi>Pj,分类讨论:

  • A i &lt; A j A_i&lt;A_j Ai<Aj,强制 A j : = A i A_j:=A_i Aj:=Ai,贡献为合法排列数 / 2 /2 /2
  • A i ≥ A j A_i\geq A_j AiAj,贡献为总排列数-(强制 A i : = A j A_i:=A_j Ai:=Aj后的合法排列数/2)

考虑按权值从大往小做,维护以位置为下标的BIT
c n t = 0 cnt=0 cnt=0的地方切开分成若干段,每段中强制 A i : = A j A_i:=A_j Ai:=Aj时, c n t A j + 1... A i cnt_{A_j+1...A_i} cntAj+1...Ai一段相当于乘上了 c n t − 1 c n t \dfrac{cnt-1}{cnt} cntcnt1,注意判断区间存在 c n t = 1 cnt=1 cnt=1的情况,


*F.01 on Tree

一种比较经典的贪心方式

设点 x x x子树内 0 0 0的个数为 f ( x ) f(x) f(x) 1 1 1的个数为 g ( x ) g(x) g(x),显然先走 f ( x ) g ( x ) \dfrac{f(x)}{g(x)} g(x)f(x)最大的点

单点的情况是固定的,可以用大根堆存储,每次弹出堆顶点 i i i

  • f a i = 0 fa_i=0 fai=0( i i i为根),则删去 i i i后继续弹堆
  • 否则 f a i fa_i fai还没有走过,根据贪心走到 f a i fa_i fai后一定会立刻进入 i i i,贡献为 g ( f a i ) f ( i ) g(fa_i)f(i) g(fai)f(i),那么 i , f a i i,fa_i i,fai就是一体的了,并查集缩成一个点后重新压入堆
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值