Day 7
开题顺序好重要啊!
复盘
8:00 开题,没有通览一遍,想试试看一道想一道的策略
翻译了一下 T1 的条件,把 or 的性质想错了;往下数位 dp 是好想的,但需要记录进位?结合那个错误的条件发现不得不子集枚举,复杂度不对
尝试了好久能不能把子集枚举魔改成高维前缀和,最终失败。准备开码那个思路时重新审了一下题,发现理解错了,根本不会有进位,那dp就简单多了
码完 9:50,好在样例都一遍过了。这时间很失败啊
看 T2,想到一个树形结构很适合来刻画这个问题,考虑叶子到根的距离,决定了某个变量的贡献是正还是负
但 dp 过程中显然无法记录树形态,去看特殊性质也没什么好写的,蚌
看了眼 T3 发现是构造,心想不会和之前某次 T2 构造一样特别难吧,跳了
看 T4,第一印象还不错,发现 操作 1,2 就是点分树板子,但很显然点分树是没办法处理子树的,往下看部分分,这一档只有 20,性价比不高
发现 n q nq nq 暴力比较好拿,再往下写个无脑线段树能再拿 10,再看 v = 0 v=0 v=0 有 20?
然后开始编,成功编出值域线段树做法,看了眼时间已经 11:00 了,决定还是码完暴力分去看 T2
仔细分析了一下,发现由于元素是任意选的,那么答案一定可以刻画成形如 ( a , b , c ) (a,b,c) (a,b,c) 的三元组,表示 a a a 个正号, b b b 个负号, c ∗ K c*K c∗K
感觉这个思路很对,继续想可以得到一个大概是 n 3 n^3 n3 的暴力 dp:直接暴力求出来所有合法三元组
尝试打表出来,发现这些三元组竟然是十分有规律的,可以 O ( 1 ) O(1) O(1) 算的那种
然后就发现会了正解:直接求出来其中的一个三元组,剩下的可以快速得到。对于多组询问,直接离线就好了
看了眼时间 11:45,逆天
写完 n q nq nq 的已经 11:55,看到有 65pts,可惜最后正解没写出来
结果是
100 + 65 + 0 + 10 = 175 , rk_49 唉
T1 有点唐了,碰到感觉很麻烦的 T1 还是应该多审审题的
T2 应该能写出来的
T3 真应该看的, n 2 n^2 n2 的思路并不难想
T4 积累到一个 BFS 序处理邻域问题的思路
题解
T4 还没订,正解有个地方不太懂,回来补
Day 8
难蚌的一场
复盘
这波采取了先看完四道题的策略,感觉 T1 还是很典,T2 像是比较套路的题,应该能做;T3 很逆天,不太会;T4 更逆天
看 T1,数据范围明显是让往贡献一次计算好的角度思考的,很快有了 n^4 的做法,但是 1 s 1s 1s 能跑 1 e 9 1e9 1e9 吗?
磕绊了一会,没什么更好的思路了,决定开写。
打表发现约束完上下界后单次询问只会循环 8 e 6 8e6 8e6 次,这不妥妥赢嘛!
9:00 调完交了,过掉了大样例,跑的飞快
开 T2,想了很久觉得不太有什么 log 做法,那么对出现次数根分的思路就很明显了
然鹅 c n t > n cnt>\sqrt n cnt>n 的计算方式想的有点垃圾,十分难以拓展(需要把求相交的容斥一步变成求包含);导致 c n t < n cnt<\sqrt n cnt<n 的即使想到 ∑ c n t 2 ≈ n n \sum cnt^2≈n\sqrt n ∑cnt2≈nn 的枚举数对做法,依然有很多限制
想了将近 2h,最后无奈暴力
中间 T3 又扫了一眼,对第一个特殊性质都没想出很好的做法,感觉是心有点慌,没静下心去手玩,其实前几档都不是很难
最后剩不到 1h 看 T4 了,对于两矩形不交,只要存在一维不交就行;但相交的话需要每一维都交,显然容斥后是更好做的,这样可以使得情况与 k k k 无关,只需要直接 k k k 次方就行
到底容斥的是什么呢?不应该是”某几个矩形交“这样笼统的钦定,这么答案永远算不对,容斥的本质还是对关系钦定
考虑一张 6 6 6 个点的图,两点连边表示钦定二者相交,这么一来有 n ( n − 1 ) 2 = 15 \frac{n(n-1)}{2}=15 2n(n−1)=15 个关系,对这样的关系钦定
然后问题在于方案数怎么求,想了好久没什么正常的 dp 思路,最后发现可以直接 dfs !应该能做 c ≤ 10 c\leq10 c≤10 的,加上别的有 60 pts
开写!最后 11:59:50 极限过掉样例交了
结果是:
100 + 30 + 0 + 25 = 155 , rk_41
T2 开始想的有点偏了,应该尝试换种计数方式的,剩下的确实是比较套路的做法
T4 算错复杂度了,感觉 4 5 n 45^n 45n 应该 n n n 多大都不会超,没想到只能跑 n = 4 n=4 n=4。顺着赛时思路往下想,很快就想到了 c = 1 e 9 c=1e9 c=1e9 可以离散化的 。。。
已经差不多推到正解了,接下来就是对搜索的一些剪枝技巧或者直接打表,让它不 TLE
T3 这种构造还是应该静下心去手玩一下的…
题解
T3 T4 没订,T4 调了调赛时代码把 n = 4 n=4 n=4 跑过了
Day 9
省流:被 T2 随机区分了
复盘
8:00 开题,还是扫了一遍四道题,T1 看上去不难,T2 想了想感觉不太好做;T3 挺神秘的;T4 数据结构?
简单推了下性质,发现最终每一段取值是能定下来的,然后感觉每一段固定不下来,贪心似乎不太对?就写了个 dp ,发现很好优化,用 st 表维护区间 and 然后双指针即可。1h 才过掉
T2,慢慢推性质,无解很好判,倒过来做就行;然后发现答案要么 n+m ,要么某行或者某列没涂色,先假设做行,列的情况是对称的
枚举没涂色的行,可以把列的状态都确定下来,接下来一行行比对,给列加一些限制
发现限制有两种:01\10 无解限制,顺序限制
前者直接扫,后者发现需要建图跑个 toposort,额,好难写啊!
这个思路应该很对,但复杂度是 n 3 n^3 n3 的,想了想应该十分跑不满,写写看
怒写 1.5h 多,5k,过掉了小样例,感觉正确性应该没问题(如果没写挂),过了
开 T3,发现其实很水,暴力的 dp 写完后转移就是一个二维偏序,树状数组维护之,20min 就写完了
抓紧时间看 T4,从本题性质的角度出发,发现 k=1 的倍增做法很简单,尝试拓展到多个区间就不会做了
想了好久也没什么进展,决定再看看离线能不能做。好吧,也不会
最后写暴力,看到有 n = 5 e 4 , 1 e 5 n=5e4,1e5 n=5e4,1e5 想试试 n 2 n^2 n2 能不能冲过去,然后就想到一个很简洁的暴力思路:每次查一个点的父亲是否被标记过,然后标记它
写完发现果然过了
n
=
7
e
4
n=7e4
n=7e4 的样例,果然是小常数选手
然后就结束了
结果是:
100 + 0 + 100 + 40 = 240 , rk_36
诶,这 T2,样例给的这么水还是一道细节多(我的做法)题,应该多手玩几组样例 hack 一下的,但没想到真直接挂完了
赛后简单调一下就有 70 了…
然而赛时还是应该多想想的,仔细分析发现 toposort 的限制其实是不用考虑的,只需要在开头判无解就行。这样就复杂度又低细节又少了
T4 其实可以先想暴力的,赛时最后打暴力时才发现了 只需要考虑父亲是否在当前区间出现过 即可,那么接下来根号分治的思路就比较自然了,这可以说是比较套路的做法
正解有点牛啊!从那个 k=1 的拓展,结合树上的性质得到的做法
题解
T4 已听懂,还没写完
Day 10
节奏比较好的一场
复盘
决定换一下策略,读题、想题,有思路后不急着写代码,四道题都能拿到一些分数后再开写
8:00 开题,看到 T1 题面贼长,感觉很神秘;T2 又是构造,不过感觉可做;T3 题面好抽象啊,手推样例虽然能推出来,但结合题面看还是感觉很别扭;T4 感觉很逆天,不过想了想 60pts 是挺好拿的
回看 T1 发现挺诈骗, R × C ≤ 2.5 × 1 0 5 R\times C\leq 2.5\times 10^5 R×C≤2.5×105 ,直接暴力染色复杂度是对的,写的 s e t set set 分段做,细节还是挺多的,9:00 交了
开 T2,容易观察出 + n , − n +n,-n +n,−n 不影响答案,可以在 m o d n mod\ n mod n 意义下做;然后是绝对值,那么不妨先钦定 b n = 0 b_n=0 bn=0,前面的数就都是 ± d i \pm d_i ±di 了,然后跑个背包,限制是要把最小的 − b i -b_i −bi 加成正数,那么记录一下和的最小值就行
需要考虑最小的 b i b_i bi 取到哪,考虑 sort 完后从大到小往背包里加数,同时统计答案。细节不多,10:00 过了样例
看 T3,不懂这个换人的过程,一直没有很好的 poly 做法,猜了个东西,感觉不是很真。想了半个小时跳了,决定先拿 T4 60pts
n 4 n^4 n4 很好写,对于新加一条边, f l o y d floyd floyd 不用重新跑,只枚举点对即可;对于树的情况推了一会,还是 n 2 n^2 n2 枚举的思路,但计算贡献可以加速到 O ( n ) O(n) O(n)。
接着继续推正解,下意识觉得不太可能是这种暴力 n 2 n^2 n2 枚举边然后 check 的思路,应该是结合什么性质直接把加边给确定下来
于是打表,猜了很多结论,都没法做。只能先写了
11:30 写完了,赶紧看 T3。又想了好久觉得还是用那个猜的结论写写吧,最后 10min 写完,发现过不去样例,就 gg 了
结果是
100 + 0 + 0 + 60 = 160 , rk_48
难蚌,T2 有个递归边界以为不用写的,没想到被 n = 2 n=2 n=2 的数据给卡了,只有这一种才能卡掉,改改就过了
赛后才发现 T3 改题面了,把那个抽象的过程描述的清晰多了,然后按题意模拟着 dp 就能有一个 n 3 n^3 n3 做法,蚌
T4 正解还是从暴力 n 2 n^2 n2 枚举出发的,数据范围小的时候还是应该往这方面想想的
不过还是感觉这把策略比较好,前面推完就知道后面哪些题好拿分了
题解
已订完
T2
妙妙的题,不过推出来了
T3
好牛的题啊!
首先看完修改后的题面,我们按题意设计一个 dp : f i , j , k f_{i,j,k} fi,j,k 表示 p p p 中考虑前 i i i 个, q q q 中考虑前 j j j 个,冲突了 k k k 个的方案数,显然对于第三维我们只关注数量即可,复杂度 O ( n 3 ) O(n^3) O(n3)
f[x][y][cnt] = 1 ;
LL ans = 0 ;
for(int i = x ; i <= n ; i ++ ) {
for(int j = y ; j <= n ; j ++ ) {
for(int k = min(i,j) ; k >= 1 ; k -- ) {
if( f[i][j][k] == 0 ) continue ;
if( pos2[p[i+1]] <= j ) f[i+1][j][k] = ( f[i+1][j][k] + f[i][j][k] ) % mod ;
else f[i+1][j][k-1] = ( f[i+1][j][k-1] + f[i][j][k] ) % mod ;
if( pos1[q[j+1]] <= i ) f[i][j+1][k] = ( f[i][j+1][k] + f[i][j][k] ) % mod ;
else f[i][j+1][k-1] = ( f[i][j+1][k-1] + f[i][j][k] ) % mod ;
}
ans = ( ans + f[i][j][0] ) % mod ;
}
}
进一步观察 打表,当
i
,
j
i,j
i,j 固定时,
k
k
k 实际上也是固定的。从这个方案构造的过程我们就能得出这一点
有: k = [ 前 i 与前 j 中冲突的个数 ] − ( i − x ) − ( j − y ) k=[前 i 与前 j 中冲突的个数] - (i-x) - (j-y) k=[前i与前j中冲突的个数]−(i−x)−(j−y) ,实际上这个是比较好观察的,我们把这个设为 k ( i , j ) k(i,j) k(i,j)
然后我们 d p dp dp 只记两维即可, f i , j f_{i,j} fi,j,此时转移变成了:
f i , j { → a n s , k ( i , j ) = 0 → f i + 1 , j , k ( i , j ) > 0 → f i , j + 1 , k ( i , j ) > 0 f_{i,j}\left\{\begin{matrix}\to ans , k(i,j)=0 \\\to f_{i+1,j},k(i,j)>0 \\\to f_{i,j+1},k(i,j)>0 \end{matrix}\right. fi,j⎩ ⎨ ⎧→ans,k(i,j)=0→fi+1,j,k(i,j)>0→fi,j+1,k(i,j)>0
进一步优化:dp 方程显然是一个典型的格路计数!
从 ( x , y ) (x,y) (x,y) 开始,每次向右或向上,走到 k ( i , j ) = 0 k(i,j)=0 k(i,j)=0 的位置就停下并贡献到答案中
还是没法直接做,分析一下性质
k ( i − 1 , j ) ≥ k ( i , j ) ≥ k ( i − 1 , j ) − 1 k(i-1,j)\geq k(i,j)\geq k(i-1,j)-1 k(i−1,j)≥k(i,j)≥k(i−1,j)−1
意思是:当 i i i 往后移动一步,冲突的数量要么不变,要么减少 1 1 1。对于 j j j 同理
这告诉我们 k ( i , j ) k(i,j) k(i,j) 具有单调性!画个图
1
1
1 代表
k
(
i
,
j
)
k(i,j)
k(i,j) 非
0
0
0 的位置
这样我们只需双指针求出外面的一圈终点的坐标,然后方案直接组合数算,显然终点数是 O ( n ) O(n) O(n) 的
注意到一个 0 0 0 的答案要减掉 到左边 0 0 0 和 到下边 0 0 0 的方案 (到 0 0 0 就停下)
牛啊
T4
看数据范围,
n
n
n 最大只有
600
600
600,那么我们可以考虑
n
2
n^2
n2 枚举后计算减量(还有一点是因为去图上找性质真发现不了什么)
n 2 n^2 n2 的计算是简单的,注意 f l o y d floyd floyd 加入一条新边或一个新点只需要 n 2 n^2 n2 枚举点对更新就行
然后考虑优化这个十分暴力的过程,设 f ( a , b ) f(a,b) f(a,b) 表示新加入 ( a , b ) (a,b) (a,b) 这条边之后贡献的减量,那么: f ( a , b ) = ∑ p , q m a x ( 0 , d ( p , q ) − d ( p , a ) − d ( b , q ) − 1 , d ( p , q ) − d ( p , b ) − d ( a , q ) − 1 ) f(a,b)=\sum_{p,q}max(0\ ,\ d(p,q)-d(p,a)-d(b,q)-1\ ,\ d(p,q)-d(p,b)-d(a,q)-1) f(a,b)=p,q∑max(0 , d(p,q)−d(p,a)−d(b,q)−1 , d(p,q)−d(p,b)−d(a,q)−1)
m a x max max 中有三个式子,很难同时维护;如果后两个式子都 > 0 >0 >0 的话比较它们的大小是不容易的
我们有一个很妙的结论:后两个式子不能同时大于 0 0 0!
换句话说,加入 ( a , b ) (a,b) (a,b) 后,黄色路径和绿色路径不可能都比原来的 d ( p , q ) d(p,q) d(p,q) 短
反证法:
加入之前, d ( p , q ) d(p,q) d(p,q) 显然可以走 p → a → q p\to a\to q p→a→q,这条路径比黄色路径长,可以得到: m < y + 1 m<y+1 m<y+1
d ( p , q ) d(p,q) d(p,q) 也可以走 p → b → q p\to b\to q p→b→q,这条比绿色路径长,那么 y < m + 1 y<m+1 y<m+1
显然矛盾
( 这种结论还是记一下吧
那么我们就可以把 m a x max max 给拆开了,设 g ( a , b ) = ∑ p , q m a x ( 0 , d ( p , q ) − d ( p , a ) − d ( b , q ) − 1 ) g(a,b)=\sum\limits_{p,q}max(0\ ,\ d(p,q)-d(p,a)-d(b,q)-1) g(a,b)=p,q∑max(0 , d(p,q)−d(p,a)−d(b,q)−1)
那么 f ( a , b ) = g ( a , b ) + g ( b , a ) f(a,b)=g(a,b)+g(b,a) f(a,b)=g(a,b)+g(b,a),直观的理解:加入无向边减少的量,等于分别按两个方向经过减少量的和
考虑怎么求 g ( a , b ) g(a,b) g(a,b),四个变量,常见优化思路是 把这些变量分成无关的部分,每个部分预处理,然后合并
g ( a , b ) = ∑ p , q m a x ( 0 , d ( p , q ) − d ( p , a ) − d ( b , q ) − 1 ) g(a,b)=\sum\limits_{p,q}max(0\ ,\ d(p,q)-d(p,a)-d(b,q)-1) g(a,b)=p,q∑max(0 , d(p,q)−d(p,a)−d(b,q)−1)
考虑枚举 b , p b,p b,p,把式子改成
∑ p , q m a x ( 0 , d ( p , q ) − d ( b , q ) − ( d ( p , a ) + 1 ) ) \sum\limits_{p,q}max(0\ ,\ d(p,q)-d(b,q)- (d(p,a)+1)\ ) p,q∑max(0 , d(p,q)−d(b,q)−(d(p,a)+1) )
设 X p , b , q = d ( p , q ) − d ( b , q ) X_{p,b,q}=d(p,q)-d(b,q) Xp,b,q=d(p,q)−d(b,q)
发现前后两部分是独立的,考虑对 X p , b X_{p,b} Xp,b 和 d ( p , a ) + 1 d(p,a)+1 d(p,a)+1 分别排序,双指针求刚好 ≥ \geq ≥ 的位置,前缀和计算之
排序不能 s o r t sort sort,这样是 n 3 log n n^3\log n n3logn 的,改成桶排就 ok 了
#include<bits/stdc++.h>
using namespace std ;
typedef long long LL ;
const int N = 610 ;
int n , m ;
vector<int> E[N] ;
int d[N][N] , ans , X[N] , sum[N] , g[N][N] ;
int id[N] , P ;
bool cmp( int x , int y )
{
return d[P][x] < d[P][y] ;
}
int cnt[N] ;
int main()
{
scanf("%d%d" , &n , &m ) ;
int x , y ;
memset( d , 0x3f , sizeof d ) ;
for(int i = 1 ; i <= n ; i ++ ) d[i][i] = 0 , id[i] = i ;
for(int i = 1 ; i <= m ; i ++ ) {
scanf("%d%d" , &x , &y ) ;
d[x][y] = d[y][x] = 1 ;
E[x].push_back(y) ;
E[y].push_back(x) ;
}
for(int k = 1 ; k <= n ; k ++ ) {
for(int i = 1 ; i <= n ; i ++ ) {
for(int j = 1 ; j <= n ; j ++ ) {
d[i][j] = min( d[i][j] , d[i][k]+d[k][j] ) ;
}
}
}
ans = 0 ;
for(int i = 1 ; i <= n ; i ++ ) {
for(int j = i+1 ; j <= n ; j ++ ) ans += d[i][j] ;
}
int res = 0 ;
for(int p = 1 ; p <= n ; p ++ ) {
P = p ;
sort( id+1 , id+n+1 , cmp ) ;
for(int b = 1 ; b <= n ; b ++ ) {
for(int q = 1 ; q <= n ; q ++ ) {
X[q] = d[p][q]-d[b][q] ;
if( X[q] >= 1 ) cnt[X[q]] ++ ;
}
int sum = 0 , ct = 0 ;
for(int i = n , j = n+1 ; i >= 1 ; i -- ) {
int a = id[i] ;
while( j-1 >= 1 && j-1 > d[p][a]+1 ) {
sum += (j-1)*cnt[j-1] ;
ct += cnt[j-1] ;
j -- ;
}
g[a][b] = ( g[a][b] + sum-(d[p][a]+1)*ct ) ;
}
for(int j = 1 ; j <= n ; j ++ ) cnt[j] = 0 ;
}
}
for(int a = 1 ; a <= n ; a ++ ) {
for(int b = a+1 ; b <= n ; b ++ ) {
res = max( res , g[a][b]+g[b][a] ) ;
}
}
int num = 0 ;
for(int a = 1 ; a <= n ; a ++ ) {
for(int b = a+1 ; b <= n ; b ++ ) {
if( res == g[a][b]+g[b][a] ) num ++ ;
}
}
printf("%d %d\n" , ans-res/2 , num ) ;
return 0 ;
}