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的主要内容,如果未能解决你的问题,请参考以下文章