E

题目HERE

题目大意

数组a[n], 其中2<=n<=100'000,-1'000'000'000<=a[i]<=1'000'000'000

a[i+1]-a[i] >= k[i] ,其中-1'000'000<=k[i]<=1'000'000

q个操作1<=q<=100'000

操作类型1,对a[i] 增加x,其中0<=x<=1'000'000,如果操作后无法满足上面与k的关系,则调整a[i+1]=a[i]+k[i] 直到所有 的值都满足

操作类型2,求sum of a[l->r]

解法

一眼就是个线段树,但是比以往遇到的线段树,要难维护一些,注意一下需要维护的状态

limiaomiao的解法

  1. [l->r] 实际的 差: der
  2. [l->r] 的差前缀和的和 :sum[l->r] = (der[l])+(der[l]+der[l+1])+...+(der[l]+...+der[r])

build tree

der[o]= der[o<<1] + der[o<<1 | 1] 记录的是a[r]-a[l]的实际差值

sum[o]= sum[o<<1] + sum[o<<1 | 1] + der[o<<1] * length of (o<<1 | 1)

add val

对a[i]增加x的操作

实际是对 a[i-1]和a[i]的差 增加,以及 对a[i]和a[i+1]的差 的减少x

这样,线段树上操作,[这样感觉会被卡,但看limiaomiao的代码 有个神奇操作,加了一个set来记录,哪些的差值和k不同] 这样每次改变值,最多能产生1个 新的和k不同的差,那么每次的时间消耗=1+消耗的不同于k的值

所以总时间 = 操作数+ 总消耗 <= 操作数+ 初始不同的个数+过程中产生的不同的个数,是线性关系

询问

ans_sum[l->r]

=a[l]+...+a[r]

= a[l]*(r-l+1)+ ((der[l])+(der[l]+der[l+1])+...+(der[l]+...+der[r]))

= getval(a[l])×(r-l+1)+query_sum(l->r)

我这里的思路

注意到实际维护的是a[i+1]-a[i]-k[i] >= 0

那么线段树,可以是在数组b[i]=a[i+1]-a[i]-k[i]上面建立

b[i]所以要全非负

build tree

der[o]= der[o<<1] + der[o<<1 | 1] 记录的是a[l->r]的差值再减去这一段k的差值保证非负

sum[o]= sum[o<<1] + sum[o<<1 | 1] + der[o<<1] * length of (o<<1 | 1)

add val

同样 对a[i]增加x的操作

实际是对 a[i-1]和a[i]的差 增加,以及 对a[i]和a[i+1]的差 的减少x

注意到这样的话,增加x的部分就可以先lazy掉,

在减少的部分,如果 der[l->r] < x 那么这一段整个der都是0,可以lazy掉,保证了每次的log级别的操作

除此以外,每次可能访问的时候,把lazy的部分向下分发一下

询问

ans_sum[l->r]

=a[l]+...+a[r]

= a[l]*(r-l+1)+ ((der[l]+k[l])+(der[l]+k[l]+der[l+1]+k[l+1])+...+(der[l]+k[l]+...+der[r]+k[r]))

= getval(a[l])×(r-l+1)+query_sum(l->r)+sum_der_k(l->r)

总结

相比之下 我这样写,线段树的lazytag的多两个lazytag,而且 对于k也要维护一个sum的线段树,更难写

题目HERE

题目大意

给你

a1 个1

a2 个2

a8 个8

求用这些数中的一部分相加,得到的 最大的 不大于W的 数为多少

其中

1<=ai<=10^16

0<=W<=10^18

解法

翻译自官方题解

假设在未知的最优解中 i 取c_i

L = lcm(1,,,,8) = 840

那么c_i可以表示为

c_i= (L/i)*P_i+q_i 根据除法余数性质可以让q满足 0<=q<L/i

(L/i)*P_i个i是L的倍数(且是1,2,3,4…倍 占满)(小学未知数乘法?)

所以只用枚举q_i的部分

然而这样 所有方案840**8/8/7/6/5/4/3/2/1=6147715553280000000时间内肯定过不了

dp[1->x types of items][weight] = 把构成weight的部分去除后 还能最多有多少个L 这里的构成

空间O(8*8L)

时间O(8L*sum(L/1+L/2+...+L/8))

ans = max{ weight + L * min(dp[0][weight],(W-j)/L) (0<=weight<=8L)

代码

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
#include <bits/stdc++.h>

using namespace std;

const int N = 9;
const int L = 840;

typedef long long li;

li dp[N][L * N];
li W;
li cnt[N];

int main()
{
cin >> W;
for(int i = 0; i < 8; i++)
cin >> cnt[i];
for(int i = 0; i < N; i++) for(int j = 0; j < L * N; j++) dp[i][j] = -1;
dp[0][0] = 0;
for(int i = 0; i < 8; i++)
{
for(int j = 0; j < L * N; j++)
{
if(dp[i][j] == -1) continue;
int b = L / (i + 1);
if(cnt[i] < b)
b = cnt[i];
for(int k = 0; k <= b; k++)
{
li& d = dp[i + 1][j + k * (i + 1)];
d = max(d, dp[i][j] + (cnt[i] - k) / (L / (i + 1)));
}
}
}
li ans = 0;
for(int j = 0; j < L * N; j++)
{
if(j > W || dp[8][j] == -1)
continue;
ans = max(ans, j + L * (min(dp[8][j], (W - j) / L)));
}
cout << ans << endl;
}

C

题目HERE

题目大意

n 个点 (1<=n<=100000)

m 条边 (1<=m<=100000)

有向图

从端点1出发,总共走d (1<=d<=50)天,每天能走一条边,能访问多少个不同的开放的点

开放时间表 n*d,0表示N号点 在D天关闭,1表示开放

解法

翻译自官方题解

新建一个图

图上的点为 ([1->n],[0->d-1]) O(nd)

如果原来题目图上有(u,v)

建立边 (u,t) -> (v,(t+1)%d) , O(m d d)

然后算 新图 的强连通分量,对每一个强连通分量缩点,计算 不同的开放的点的个数 = cost

在新的DAG上求最长路

假设一个点u 在新图中拆成 (u,j1)和(u,j2),也就是u 能通过某些边走到j1,注意到,新图里的边只和原图是否连通有关,所以 (u,j2) 能走到(u,(j2+(j2-j1))%d) 沿着j1->j2的路径,沿着这条路径走d-1次,那么走到(u,(j2+(d-1)(j2-j1))%d) = (u,j1+d*(j2-j1)%d) = (u,j1) 所以如果(u,j1)和(u,j2)弱连通,则它们强连通

在新的DAG(缩点后的)上 不会存在2个 u

在DAG有向无环图上 做dp

代码

待补

D

题目HERE

题目大意

交互题

1<= t,c,(t+c)<=1000

有一条链表,这个链表上有一个环,其中环的部分长度为c,非环的部分长度为t

从非环的部分开始走,目的地是 环和非环的交界处,也就是环的入口(环上)

不知道t和c

一共有10个单位可以用来走

交互次数应当<=3(t+c)

交互:

每次提供 需要移动的单位列表,比如2 4 5,那么意味着 2号 4号 5号 沿着链表跳向下一个节点

每次操作返回 哪些单位在同一个点上,如129 3745 680 表示,1号2号9号在一个点上,3.7.4.5在一个点上..

当你认为所有点都走到入口上的时候,输出done

解法

分成 甲 乙 丙

甲1步
乙2步

t步,乙2*t步在环上,变成环上追击问题,距离为 (c-(2t-t)%c)%c = (c-t%c)%c

然后,甲乙丙,都走1步

当丙进入环时,步数为t

甲和乙相遇后一起走,所以位置相同,甲的步数这时候为 (t+ (c-t%c)%c)+t

注意到(t+ (c-t%c)%c)是c的倍数,也就是,丙刚进入时,甲和乙也同时走到了环的入口

总耗时2(t+(c-t%c)%c)+t = 3t + 2(c-t%c)%c < 3t+3c = 3(t+c)

解了

题目HERE

题目大意给

01串 长度<=100

每次可以任选一段连续的相同数字的串删掉,删掉的代价为cost[长度],删掉后 原来的串两端相连,求这样操作直到删空,cost和的最大值

如 0011100 删掉中间连续3个1 变成0000,并且获得cost[3]

样例

1
2
3
7
1101001
3 4 9 100 1 2 3

输出109

1101001 → 111001 → 11101 → 1111 → ∅.

解法

状态 dp[开始index][结束index(不包括)][?->开始index 相同的数字的长度len]

[开始index,结束index)且开始的左边还有 len-1个和s[开始index]相同的数字

状态转移

dp[start][end][len] = max(A[len]+dp[start+1][end][1],max(dp[start+1][itr][1]+dp[itr][end][len+1])), 其中s[itr] == s[start]

举例解释

1
2
3
4
5
6
11111100001111011
yyyy
s e
i
4=len
xxxxxx

上面假设在求 dp[s][e][len]

那么A[len]+dp[s+1][end][1]表示先一次处理掉yyyy对应部分 再处理s以后的部分

那么dp[s+1][i][1]表示把xxxxx对应部分处理掉的代价,dp[i][e][len+1]表示的是 处理掉xxxxx这部分后,在处理剩余部分的代价

所以最终,答案就是dp[0][N][1]

状态O(n^3)状态转移O(N)所以总时间复杂度 O(N^4)

code

题目HERE

求一个n x m <= 300 000的二维字符数组

满足任何2x2的字符都包含A T G C

并且和给你的 数组不同的字符尽量少

我的思路

我有发现如果已经有

1
2
AG
TC

那么右侧一列,一定是AT,顺序不定

下面两个一定是AG顺序不定,


又发现如果对于任意2*3

1
2
XYZ
???

如果XYZ出现了3个字母

那么???下方的一定是XYZ且顺序不变

因为如果Y不在中间,那么会变成

1
2
3
XYZ
???
Y?Y

第3行填X 或 Z都是不行的


又发现2x3如果XYZ 3个字母不同,具有性质,

1
2
XYZ
???

下面的??? 是确定的,因为X列和Z列是相等的,所以一定是,其中O表示第4个字母

1
2
XYZ
ZOX

然后我就现在想如何构造 去找,因为找到3个不同,那么整列都确定了


然后就自闭了

其实可以把上面的事实展开

1
2
3
4
5
6
7
8
XYZ
ZOX
XYZ
ZOX
XYZ
ZOX
XYZ
ZOX

有结论,如果 某中出现连续3个不同字符,那么这3个字符对应的,每包含且仅包含两个字符,且任意行 包含连续这3个不同字符

交换行列有,如果 某中出现连续3个不同字符,那么这3个字符对应的,每包含且仅包含两个字符,且任意列 包含连续这3个不同字符

注意到上面两条是事实,但是是互斥的

那么 目前有3种情况

  1. 所有行列,的每一个行列,仅仅包含两个字符[且两两交替.废话]
  2. 存在一列 包含3个不同字符,那么所有列都包含这3个不同字符,所有行的每一行仅仅包含两个字符,
  3. 和2把 行列换一下

综上↓

以下是一句话题解

如果你发现了事实:

要么每一行 只有两种字母

要么每一列 只有两种字母


没了 然后暴力枚举就完了 这个题评分果然有2300,感觉这种题又是需要多枚举几个比如能枚举到5*5的能看出规律就好了

0%