标签:nod 博弈 最大 dev line namespace 草稿 mod 编译
\(A,B,C\)都没印象了...所以就只放我写过题解的D,E了...
如果有时间会去补一下F之类的,不过我不会博弈论啊QAQ
`
CF1197D 【Yet Another Subarray Problem】
早上打算做一做的时候挂在自己的草稿纸的潦草字迹上了,挂了1个小时(虽然编译器也有一点锅,果然学校的Dev还是有点小撮,调一年)
我们仔细康康原来的式子:
\[\sum_{i=l}^ra[i] - k*\left\lceil\dfrac{r - l+1}{m}\right\rceil\]
尝试用一下前缀和,记\(x = l -1\)
则原式即:
\[sum[r] -sum[x]-k*\left\lceil\dfrac{r-x}{m}\right\rceil\]
考虑对\(r,x\)分别讨论,将其改写成这种形式:
\[r = \left\lfloor\frac{r}{m}\right\rfloor*m + r\%m\]
\[x = \left\lfloor\frac{x}{m}\right\rfloor*m + x\%m\]
则原式即:
\[sum[r]-sum[x]-k*\left\lceil\dfrac{\left\lfloor\frac{r}{m}\right\rfloor*m + r\%m-(\left\lfloor\frac{x}{m}\right\rfloor*m + x\%m)}{m}\right\rceil\]
也就是:
\[sum[r]-sum[x]-k*\left\lceil\ \left\lfloor\frac{r}{m}\right\rfloor - \left\lfloor\frac{x}{m}\right\rfloor+ \frac{( r \% m - x \% m )}{m}\right\rceil\]
我们发现前两个貌似都是整数,他们不会影响上取整的结果,所以我们可以将他们提出来,得到:
\[sum[r]-k*\left\lfloor\frac{r}{m}\right\rfloor-(sum[x]-k*\left\lfloor\frac{x}{m}\right\rfloor)-k*\left\lceil\dfrac{r\% m-x\%m}{m}\right\rceil\]
如果我们记\(D(x) = sum[x] - k*\left\lfloor\dfrac{x}{m}\right\rfloor\)
那么原式即:
\[D(r) -D(x)-k*\left\lceil\dfrac{r\% m-x\%m}{m}\right\rceil\]
我们发现后面那一坨貌似和其他值均无关系,它只和\(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\n", Ans ) ;
return 0 ;
}
然而实际上这个复杂度可以更优,不过貌似出题人没有给非常大的\(m\)而已\(.......\)
这里讲一下本蒟蒻\(yy\)出来的做法吧(虽然听说有\(O(n)\)做法)
我们注意到\(x\%m<m\),所以\(1 \ge\left\lceil\dfrac{r\% m-x\%m}{m}\right\rceil \ge 0\)
如果我们设\(i\%m = t~(m> t\ge 0)\)
那么使得\(\left\lceil\dfrac{t-y}{m}\right\rceil = 0\)的\(y\)是可以直接求出来的。
即\(y\ge t\)
对于其余情况,\(\left\lceil\dfrac{t-y}{m}\right\rceil = 1\)
接下来有一个更加优秀的性质:
注意到我们求解每个以\(i\)为结尾的最优答案的时候,其\(i\)连续,换而言之\(i\%m\)的值连续(\(0,1,2...m-1\))
到达\(m-1\)后变成\(0\)
我们考虑按照\(\left\lceil\dfrac{i\%m-x\%m}{m}\right\rceil\)是否等于\(1\)将可转移的\(D(x)\)分成两段。
对于两部分各开一个线段树,维护一下最小值即可。
左边的线段树表示\(\left\lceil\dfrac{i\%m-x\%m}{m}\right\rceil=0\)的\(D(x)\),右边则是\(\left\lceil\dfrac{i\%m-x\%m}{m}\right\rceil=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(n\log 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)\)
我们注意到转移的条件是\(1\le 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\n", Ans % mod ) ;
return 0;
}
标签:nod 博弈 最大 dev line namespace 草稿 mod 编译
原文地址:https://www.cnblogs.com/Soulist/p/11656247.html