Educational Codeforces Round 117

CF tracker

Edu List

https://codeforces.com/contest/1612

F. Armor and Weapons

n个盾,m个武器

power = 盾idx + 武器idx

有q个 idx组合是加强的 power = qi(盾idx) + qj(武器idx) + 1(比沒有加強的多1)

初始,(盾1,武器1)

如果想得到 盾k 或者武器k, 那么power 需要>= k

同时只能持有两个, 所以之前得到过但是放弃的不能用于组合 也就是(a,b) -> (a, <= a+b+(?1)) or ( <= (a+b+?1), b)

问(1,1) -> (n,m) 的最小次数

范围

n,m 2e5

q 2e5

2s 512mb

我的思路

dp[m][n] = min(dp[m][n-m ~ n-1],dp[m][n-m-1] if (m,n-m-1)在q中), 对于另一侧固定,类似的

从小到大 的角度, 在沒有q的情況,就是(1,1) -> (1,2) -> (3,2) -> (3,5) -> (8,5) 這樣是最快的

如何處理有q的?

如果没有上界 (a,b)->(a,a+b+1), 那么如果继续固定a, 那么显然 (a,a+b+1) 不会比(a,a+b) 差, 因为(a,a+(a+b+1)) 都可达

问题是如果需要(a,a+b) -> (a+a+b,a+b)

既然如此,又注意到不使用q都是log级别的次数

直接dp空间不够

所以变成

dp[a][step] = maxb

也就是求min step, 使得 dp[m][step] = n


有个问题是满足局部性吗

似乎不会证明

感觉需要改一改

dpa[a][step] = maxb 为最后一次是固定a 得到的maxb

dpb[b][step] = maxa 为最后一次是固定b 得到的maxa

dpa[a][step] = max(a+dpa[a][step-1]+(?1), a+wb+(?1) (dpb[wb][step-1] >= a))

dpb 同理

问题 对于给定 step, dpa内有单调性吗

似乎可以归纳一下,设给定step, dpa和dpb都是非严格单调递增,

那么step+1时,如果dpa[step+1][a] > dpa[step+1][a+1]

a+dpa[step][a]+1 <= (a+1) + dpa[step][a+1], 所以上一次不是固定a,

如果是固定b:

有额外点数 b+dpb[step][b](==a-b-1)+1 (更大则 都是b,更小则当前不可达), 只要(b+1)+dpb[step][b+1](>=a-b-1) 存在

无额外点数 b+dpb[step][b](==a-b) (更大则 都是b,更小则当前不可达), 只要(b+1)+dpb[step][b+1](>=a-b) 存在

但注意到这种情况是 a > b 那么必定比这种情况是 之前a不存在(否则之前存在一定是通过a而不是通过b),所以只能通过b到达,所以得证

然后需要注意的是,如果其中一个很小,那么就需要 固定一个反复跳另一个, 这个时候就不要暴力了


写了一下发现并没有单调性

4次,(1,1)(1,2)(3,2)(3,5)(3,8)

4次,(1,1)(1,2)(3,2)(3,5)(8,5)

但4次,4的话最大只能得到7


所以还是暴力吧

代码

调了很久过了

https://codeforces.com/contest/1612/submission/193227902

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,n) for(ll i=(a);i<(ll)(n);i++)
#define per(i,a,n) for(ll i=(n);i-->(a);)
ll read(){ll r;scanf("%lld",&r);return r;}

vector<int> a2b[200010];
const int PWR = 30; // fib(30) > 2e5

int main(){
ll n=read();
ll m=read();
vector dpa(PWR,vector<ll>(n+1,0)); // dpa[step][a] = maxb
vector dpb(PWR,vector<ll>(m+1,0)); // dpb[step][b] = maxa
int q=read();
while(q--){
int a=read();
int b=read();
a2b[a].push_back(b);
}
rep(a,1,n+1) if(a2b[a].size()>1) sort(begin(a2b[a]),end(a2b[a]));

dpa[0][1] = 1;
dpb[0][1] = 1;
auto hasab = [&](int a,int b) -> bool{
int ib = lower_bound(begin(a2b[a]),end(a2b[a]),b) - begin(a2b[a]);
return ib < (int)size(a2b[a]) and a2b[a][ib] == b;
};
auto hasba=[&](int b,int a){return hasab(a,b);};
rep(step,1,PWR){
auto update=[&](ll na,ll nb,auto &va,const auto&vb,auto has){
rep(a,1,na+1){
ll b = va[step-1][a];
if(b == 0) break;
va[step][a] = min(a+b+has(a,b),nb);
}
vector<ll> tmp(na+1,0);//a2b
rep(b,1,nb+1){
ll a = vb[step-1][b];
if(a == 0) break;
tmp[a] = max(tmp[a],b);
}
ll b = 0;
per(a,1,na+1){
if(tmp[a] == 0 and b == 0) continue;
b = max(b,tmp[a]);
va[step][a] = max(va[step][a], min(a+b+has(a,b),nb));
}
};
update(n,m,dpa,dpb,hasab);
update(m,n,dpb,dpa,hasba);
if(dpa[step][n] == m || dpb[step][m] == n) {
printf("%lld\n",step);
return 0;
}
}
auto out=[&](ll na,ll nb,ll b,auto has){
int ans=PWR-1;
while(b < nb){
b = min(nb, na + b + has(na,b));
ans++;
}
printf("%d\n",ans);
};

if(n < m){
out(n,m,dpa[PWR-1][n],hasab);
}else{
out(m,n,dpb[PWR-1][m],hasba);
}
return 0;
}

G. Max Sum Array

给长m数组c

需要构建 数组a,包含[1..m] 其中i出现c[i]次

f(a) = sum(j-i) , 其中a[i] = a[j], i < j

求max f(a), 和能得到这个max值的不同a的个数 mod 1e9+7

范围

m 5e5

ci [1,1e6]

2s

256mb

我的思路

显然 不同值之间不会影响贡献, 只是插入到之间影响距离会影响贡献

那么对于同样的a, 贡献 = 最大-最小

1
ijk.........kji

显然是一个方案, 是否有办法更大?

只出现一次的全部放在最中间

显然 出现>=2次的如果有x个,那麼前缀x个和后綴x个一定要每个出现且只出现一次, 除了前缀和后缀,对中间的值的放置无影响

否则如果有不出现,的和重复出现的做交换一定 交换后更优

而出现了的 根据排序交换原理,总可以通过相邻交换得到

而注意到 如果 ...ij...].......[..ki...ki 交换, 那么i的贡献-1,而k的贡献+1 总贡献不变

所以这就是最大值且无法得到更大的

那么答案就是 x!(前缀) * x!(后缀) * binom(n-2x,c[i]-2?...)

似乎就没了


想得有点问题, 并不是最大和最小之差,因为是想等的两两之差

那么 posj-posi 被计算了 左边相等次数 * 右边相等次数

似乎相互影响的会变复杂

如果依然考虑交换呢

对于颜色 a和b

1
2
a....ba..baa..b.aa
x x

如果 交换a和b, 那么变化也是复杂的

如果先选定相邻不同颜色的位置, 再考虑颜色,和交换

1
2
3
4
a....ba..baa..b.aa
xx
la ra
lb rb

那么 对于a来说 左侧-1右侧+1, -la*(ra+1) + (la+1)*ra = ra-la

那么 对于b来说 左侧+1右侧-1, lb*(rb+1) - (lb+1)*rb = lb-rb

也就是判断 (ra-la) - (rb-lb) >= 0ra-la >= rb - lb

注意同色点 对应的r-l 也就是 5 3 1 -1 3 5 或者 4 2 0 -2 4 的形状

所以它不至有局部性也有全局性

似乎就没了


然后注意ci * m 还是比较大,所以前缀和处理一下

算最大值同样,不妨直接从长到短放, 这样能保证距离一致

那么 +1~+3 的距离 就是 len(1) + len(2) 再乘上贡献和

t = (+1+3)/2 = 2

对于长度c 的贡献为 (c-t)(c+t)/4 = 1/4c^2 - 1/4t^2

所以需要知道多少个比它大,不同奇偶的 ci, 的 c^2/4和 与 -个数* t^2/4

类似的 -3~-1的距离 是 len(-3)+len(-2) -1

代码

https://codeforces.com/contest/1612/submission/193242305

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MOD 1000000007
#define rep(i,a,n) for(ll i=(a);i<(ll)(n);i++)
#define per(i,a,n) for(ll i=(n);i-->(ll)(a);)

ll read(){ll r;scanf("%lld",&r);return r;}
const int C = 1000000;
ll c[500010];
ll fac[C+10]={1};
ll qpow(ll b,ll p){
ll r=1;
while(p){
if(p%2) (r*=b)%=MOD;
(b*=b)%=MOD;
p/=2;
}
return r;
}

int main(){
int m=read();
rep(i,0,m) c[i]=read();
rep(i,1,C+1) fac[i]=fac[i-1]*i%MOD;
vector<ll> pres(C+10,0);
rep(i,0,m){
pres[(c[i] - 1)%2] += 1;
pres[c[i] + 1] -= 1;
}

ll ans = 1;
rep(i,0,C+1){
if(i > 1) pres[i] += pres[i-2];
if(i==0) (ans *= fac[pres[i]])%=MOD;
else (ans *= fac[pres[i]]*fac[pres[i]]%MOD)%=MOD;
}
vector gesqsum(C+10,0);
vector gecnt(C+10,0);
ll inv4 = qpow(4,MOD-2);
rep(i,0,m){
gecnt[c[i]]++;
(gesqsum[c[i]]+=c[i]*c[i]%MOD)%=MOD;
}
per(i,0,C){
(gesqsum[i]+=gesqsum[i+2])%=MOD;
gecnt[i]+=gecnt[i+2];
}

ll len = 0;
rep(i,0,C+1){
{ // i->i+2, [i]+
ll dis = pres[i]+pres[i+1];
ll cnt = (gesqsum[i+3]*inv4 - gecnt[i+3]*((i+1)*(i+1)%MOD)%MOD*inv4%MOD)%MOD;
(len+=dis*cnt%MOD)%=MOD;
}
if(i==0) continue;
{ // -i->-i+2
ll dis = pres[i]+pres[i-1];
ll cnt = (gesqsum[i+1]*inv4 - gecnt[i+1]*((i-1)*(i-1)%MOD)%MOD*inv4%MOD)%MOD;
(len+=dis*cnt%MOD)%=MOD;
}
}

printf("%lld %lld\n",(len+MOD)%MOD,ans);
return 0;
}

总结

都可以自己过,就是编码太慢

F. 虽然能自己写出来 但是写了半天

G. 也自己把它过了,而且感觉细节比F好想很多

参考

官方