CF1197div2

Posted soulist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1197div2相关的知识,希望对你有一定的参考价值。

(A,B,C)都没印象了...所以就只放我写过题解的D,E了...

如果有时间会去补一下F之类的,不过我不会博弈论啊QAQ
`
CF1197D 【Yet Another Subarray Problem】

早上打算做一做的时候挂在自己的草稿纸的潦草字迹上了,挂了1个小时(虽然编译器也有一点锅,果然学校的Dev还是有点小撮,调一年)

我们仔细康康原来的式子:

[sum_{i=l}^ra[i] - k*leftlceildfrac{r - l+1}{m} ight ceil]

尝试用一下前缀和,记(x = l -1)

则原式即:

[sum[r] -sum[x]-k*leftlceildfrac{r-x}{m} ight ceil]

考虑对(r,x)分别讨论,将其改写成这种形式:

[r = leftlfloorfrac{r}{m} ight floor*m + r\%m]

[x = leftlfloorfrac{x}{m} ight floor*m + x\%m]

则原式即:

[sum[r]-sum[x]-k*leftlceildfrac{leftlfloorfrac{r}{m} ight floor*m + r\%m-(leftlfloorfrac{x}{m} ight floor*m + x\%m)}{m} ight ceil]

也就是:

[sum[r]-sum[x]-k*leftlceil leftlfloorfrac{r}{m} ight floor - leftlfloorfrac{x}{m} ight floor+ frac{( r \% m - x \% m )}{m} ight ceil]

我们发现前两个貌似都是整数,他们不会影响上取整的结果,所以我们可以将他们提出来,得到:

[sum[r]-k*leftlfloorfrac{r}{m} ight floor-(sum[x]-k*leftlfloorfrac{x}{m} ight floor)-k*leftlceildfrac{r\% m-x\%m}{m} ight ceil]

如果我们记(D(x) = sum[x] - k*leftlfloordfrac{x}{m} ight floor)

那么原式即:

[D(r) -D(x)-k*leftlceildfrac{r\% m-x\%m}{m} ight ceil]

我们发现后面那一坨貌似和其他值均无关系,它只和(r\%m)(x\%m)有关

至此,我们已经将原式化简的差不多了。


(休息休息)


我们考虑求解对于每个(i)以其为结尾的最大权值。

不难发现,对于此(i),我们需要求解的式子中(D(i))是固定的,(i\%m)也是固定的,所以我们可以考虑枚举一个(x\%m),并统计对于(\%m)相同的(x)中维护一个最小(D(x)),就可以保证权值最小了,复杂度(O(nm))

详见代码:

#include<bits/stdc++.h>
using namespace std ;
#define int long long
int read() {
    char cc = getchar() ; int cn = 0, flus = 1 ;
    while( cc > '9' || cc < '0' ) { if( cc == '-' ) flus = -flus; cc = getchar() ; }
    while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ; 
    return cn * flus ;
}
const int inf = 1234567890000 ;
const int N = 3e5 + 5 ; 
const int M = 15 ; 
int a[N], D[N], n, m, k, Mi[M], Ans ; 
signed main() {
    n = read(), m = read(), k = read() ;
    for( int i = 1; i <= n; ++ i ) 
    a[i] = read(), a[i] = a[i - 1] + a[i], D[i] = a[i] - k * ( i / m ) ; 
    for( int i = 0; i < m; ++ i ) Mi[i] = inf ;
    Mi[0] = 0 ; int f = 0;
    for( int i = 1; i <= n; ++ i ) {
        f = - inf ; 
        for( int j = 0; j < m; ++ j ) {
            int fl = ceil( 1.0 * ( ( i % m ) - j ) / m ) ; 
            f = max( f, D[i] - Mi[j] - k * fl ) ; 
        }
        Mi[i % m] = min( Mi[i % m], D[i] ) ;
        Ans = max( Ans, f ) ;
    }
    printf("%lld
", Ans ) ;
    return 0 ;
}

然而实际上这个复杂度可以更优,不过貌似出题人没有给非常大的(m)而已(.......)

这里讲一下本蒟蒻(yy)出来的做法吧(虽然听说有(O(n))做法)

我们注意到(x\%m<m),所以(1 geleftlceildfrac{r\% m-x\%m}{m} ight ceil ge 0)

如果我们设(i\%m = t~(m> tge 0))

那么使得(leftlceildfrac{t-y}{m} ight ceil = 0)(y)是可以直接求出来的。

(yge t)

对于其余情况,(leftlceildfrac{t-y}{m} ight ceil = 1)

接下来有一个更加优秀的性质:

注意到我们求解每个以(i)为结尾的最优答案的时候,其(i)连续,换而言之(i\%m)的值连续((0,1,2...m-1)

到达(m-1)后变成(0)

我们考虑按照(leftlceildfrac{i\%m-x\%m}{m} ight ceil)是否等于(1)将可转移的(D(x))分成两段。

对于两部分各开一个线段树,维护一下最小值即可。

左边的线段树表示(leftlceildfrac{i\%m-x\%m}{m} ight ceil=0)(D(x)),右边则是(leftlceildfrac{i\%m-x\%m}{m} ight ceil=1)(D(x))

那么两边分别取出最小值然后判断一下转移给(i)即可。

然后(i)右移一位,我们发现它在(\%m)的意义下增大了(1)

那么它对应的就是在(\%m)意义下使得那个式子(=1)(D(x))多了一个。

所以我们将使得那个式子(=1)的部分中将(i\%m)删掉即可,并加入右边。

到达(m-1)的时候特判即可。

即每移动一位需要删除/加入一个点,然后还要做一遍查询,所以复杂度(O(n log m))

至于到了(m-1)后,再(+1)则发现(0/1)的线段树恰好交换

复杂度(O(nlog m))

CF1197E 【Culture Code】

可能还比较套路???

(First:)

我们先按照(out)值排序

然后考虑记(dp[i])(dp)到第(i)位的最小值,(g[i])表示得到第(i)位的最小值的方案数

那么就有:(dp[i] = min_{j=1}^{i-1}(dp[j] + in[i] -out[j] ~(out[j]le in[i])))

方案数可以顺便统计(两个dp值相等就合并)

这样做是(O(n^2))


考虑优化:

我们注意到转移的条件是(1le out[x] le in[i])

即要从([1,~in[i]])这个区间内转移过来

其次是转移方程为(dp[j]-out[j]+in[i])

最后哪一项只和(i)有关,而前面又之和(j)有关。

所以我们不难想到对(out)值建一棵线段树,我们每求出一个(dp)都丢到对应的(out)这个下标去。(把$dp[x] - out[x] $作为一个整体丢进去)

然后每次做(dp)就从([1,~in[i]])中取出最小值转移就可以了。

方案数其实就是这个最小值出现的次数

我们用线段树实现就可以了,每次合并两个儿子区间内信息即可:


左儿子小取左儿子,右儿子小取右儿子,否则相等,方案等于两边的和。

于是就只需要兹娃单点改和区间问了。。。

详见代码:

注意要离散化。。。以及取膜

#include<bits/stdc++.h>
using namespace std;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define re register
#define ls(x) x * 2
#define rs(x) x * 2 + 1
#define inf 12345678900
#define int long long
int read() {
    char cc = getchar(); int cn = 0, flus = 1;
    while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    return cn * flus;
}
const int mod = 1e9 + 7 ;
const int N = 2e5 + 5 ; 
int n, dp[N], g[N], Mi, Mx, cnt ; 
struct node {
    int in, out, in2, out2 ; 
} a[N];
struct Node {
    int val, sum ; 
};
struct S {
    int val, id, ip ;
} s[N * 2] ;
struct Tree {
    int mi, sum ; 
} tr[N * 5];
bool cmp( node x, node y ) {
    return x.out < y.out ;
}
bool cmp2( S x, S y ) {
    return x.val < y.val ; 
}
void build( int x, int l, int r ) {
    tr[x].mi = inf ; 
    if( l == r ) return ;
    int mid = ( l + r ) >> 1 ;
    build( ls(x), l, mid ), build( rs(x), mid + 1, r ) ;
}
Node up( Node lx, Node rx ) {
    if( lx.val < rx.val ) return lx ;
    if( rx.val < lx.val ) return rx ;
    return (Node){ lx.val, ( lx.sum + rx.sum ) % mod } ;
}
Node query( int x, int l, int r, int ql, int qr ) {
    if( ql <= l && r <= qr ) return (Node){ tr[x].mi, tr[x].sum } ;
    int mid = ( l + r ) >> 1 ;
    if( mid >= qr ) return query( ls(x), l, mid, ql, qr ) ;
    if( mid < ql ) return query( rs(x), mid + 1, r, ql, qr ) ;
    return up( query( ls(x), l, mid, ql, qr ), query( rs(x), mid + 1, r, ql, qr ) ) ;
}
void update( int x, int l, int r, int wh, int val, int sum ) {
    if( l == r ) {
        if( tr[x].mi == val ) tr[x].sum += sum, tr[x].sum %= mod ; 
        if( tr[x].mi > val ) tr[x].mi = val, tr[x].sum = sum ;
        return ;
    }
    int mid = ( l + r ) >> 1 ; 
    if( mid >= wh ) update( ls(x), l, mid, wh, val, sum ) ;
    else update( rs(x), mid + 1, r, wh, val, sum ) ;
    tr[x] = tr[ls(x)] ;
    if( tr[x].mi == tr[rs(x)].mi ) tr[x].sum += tr[rs(x)].sum ; 
    if( tr[rs(x)].mi < tr[x].mi ) tr[x] = tr[rs(x)] ;
    tr[x].sum %= mod ;
}
signed main()
{
    n = read() ; 
    rep( i, 1, n ) a[i].out = read(), a[i].in = read(), Mx = max( Mx, a[i].in ), 
    s[++ cnt].id = i, s[cnt].ip = -1, s[cnt].val = a[i].in, 
    s[++ cnt].id = i, s[cnt].ip = 1,  s[cnt].val = a[i].out ; 
    sort( s + 1, s + cnt + 1, cmp2 ) ; int num = 0 ;
    rep( i, 1, cnt ) {
        if( s[i].val != s[i - 1].val ) ++ num ;
        if( s[i].ip == 1 ) a[s[i].id].out2 = num ;
        else a[s[i].id].in2 = num ; 
    }
    sort( a + 1, a + n + 1, cmp ) ; 
    Mi = inf ; build( 1, 1, num ) ;
    rep( i, 1, n ) {
        dp[i] = a[i].in, g[i] = 1 ; 
        Node x = query( 1, 1, num, 1, a[i].in2 ) ;
        if( dp[i] == x.val + a[i].in ) g[i] += x.sum, g[i] %= mod ;
        if( dp[i] > x.val + a[i].in ) dp[i] = x.val + a[i].in, g[i] = x.sum ;
        if( a[i].out > Mx ) Mi = min( Mi, dp[i] ) ; 
        update( 1, 1, num, a[i].out2, dp[i] - a[i].out, g[i] ) ;
    }
    int Ans = 0 ;
    rep( i, 1, n ) {
        if( a[i].out > Mx && Mi == dp[i] ) Ans += g[i], Ans %= mod ; 
    }
    printf("%lld
", Ans % mod ) ;
    return 0;
}

以上是关于CF1197div2的主要内容,如果未能解决你的问题,请参考以下文章

cf386(div2)大一狗ACM之路

$CF 639 (Div2)$

$CF 639 (Div2)$

# $CF 638 (Div2)$

CF#603 Div2

CF1169(div2)题解报告