CF 875F
对于一个公主,我们建边连接两个王子,边权为这个公主的权值。这样一个子图合法当且仅当每个边都能找到一个点配对,并且每个点只被用至多一次。
发现合法的形态只有基环树森林(树林是基环树森林的一种特别情况)。这里有个结论是基环树森林的求法也可以对边排序后直接贪心。也就是维护每个连通块是否已经是基环树,如果当前边在一个连通块并且这个块是树就可以将其变成基环树;合并不同集合的边的时候不能合并两个基环树。
CF 1239B
处理前缀和问题(如括号序列)可以用折线图辅助理解。。
对只有一种括号的括号序列的判定:设 (
为 $1$,)
为 $-1$,求前缀和 $s_i$。答案合法当且仅当 $s_n = 0$ 且 $\forall i \in [1,n],s_i \geq 0$。
考虑将前缀和的变化画在坐标系里,发现 (
对应向量 $(1,1)$,)
对应向量 $(1,-1)$。如果选择某个位置 $i$ 的前缀位移到最后面,相当于以这个点重新建系(因为重新建系后的第一个点就是没有被位移的第一个点)。
我们发现对于这个题,输入的串循环位移对答案是没有影响的,我们先转成一个合法的括号序列,具体实现是将前缀和最小的前缀转到后面去。定义一个括号序列的权值是这个括号序列有多少种不同的循环位移方式满足它是一个合法的括号序列,我们发现转任意一个最小值都可以得到一个合法的括号序列,所以一个括号序列的权值就是最小值个数。
然后我们考虑交换括号对前缀和序列的贡献:交换相同种类括号对答案无影响。交换左边的一个 (
和右边的一个 )
对前缀和的影响是区间 $-2$;交换左边的 )
和右边的 (
对前缀和的影响是区间 $+2$。
区间 $+$ 显然不优,因为这样会抬高部分的最小值;所以我们只需要考虑第二种情况。
我们让一个区间减之后有两种情况:最小值是 $0$ 或者最小值是 $-1$ (最小值是 $-2$ 显然答案不会变优)
最小值为 $0$ 就可以处理出最小值 $\geq 2$ 的连续段(因为跨越了 $0$ 最小值变成 $-1$,答案不优),答案是这个区间内 $2$ 的个数加上剩下区间内 $0$ 的个数;
最小值为 $-1$ 可以处理出最小值 $\geq 1$ 的连续段,答案是这个区间内 $1$ 的个数。分别统计即可。
[NOI2020]制作菜品
NOI 现场想出来但是 MLE 的就我一个吧。。
NOI现场想法:这个数据范围 $m \geq n-1$ 和 $m \geq n-2$ 很有东西。观察大样例 $2$,$m \geq n-1$ 好像就是每次贪心找最小值拼起来,$m \geq n-2$ 好像划分成了两个不交集合分开做,写了个bitset忘记改空间完美MLE。。
首先我们证明 $m \geq n-1$ 一定有解:
我们将 $d$ 从小到大排序。
如果 $m \geq n$ ,那么一定有 $d_n \geq k$,因为 $\sum_{i=1}^n d_i \geq nk \Rightarrow \frac{\sum_{i=1}^n d_i}{n} \geq k$,那么一定有数 $\geq k$,我们直接选这个,递归成了 $(n,m-1)$ 的问题,我们这样做一直到问题 $(n,n-1)$。
如果 $m = n-1$,那么一定有 $d_1+d_n \geq k$。因为 $d_1+d_n =(n-1)k-\sum_{i=2}^{n-1}d_i \geq (n-1)k-\sum_{i=2}^{n-2}k \geq k $。并且 $d_1$ 一定能被删除,所以变成了子问题 $(n-1,m-1)$,归纳证明可得。
那么 $m=n-2$ 咋做呢?观察大样例不难发现将其分成两个形如 $m=n-1$ 的问题就行了,也就是要求选出一个集合 $S$,满足 $\sum_{x \in S} d_x = (|S|-1)k$。然后就能分开贪心了。
但是这样 dp 要记一个次数,不是很好。我们移项可以得到 $\sum_{x \in S} (k-d_x) = k$,就可以直接背包了。
这一部分我们注意到 $m=n-2$,所以 dp 总复杂度 bitset 优化后是 $O(\frac{n^2k}{\omega})$,而不是 $O(\frac{nk^2}{\omega})$,怀疑是我考场时候搞错了开大了导致 MLE 。。。
关于 $m=n-2$ 的证明:
充分性显然:因为划分成了两个上面证明过的可解问题。
必要性:考虑构造一张 $n$ 个点的图 $G$,如果用了一组菜就在这组菜之间连边,发现有至多 $n-2$ 条边,所以图 $G$ 一定不连通。考虑某一个连通块,一定满足 $m' \geq n'-1$,并且我们发现这个联通块的和必须恰好是 $m'k$,剩下的连通块点数是 $n-n'$ 边数 $\leq n-2-m'$,和是 $(n-2-m')k$。有解必须要满足边数等于和,所以 $m'=n'-1$。
CF 1025E
一个麻烦的构造是注意到操作可逆,于是移动到一个固定的位置即可。
但是有一个非常简便的构造:我们首先考虑将按照 $x$ 坐标排序,设原来的 $id$ 是 $ID_i$,我们将每个点移动到 $(i,ID_i)$。具体做法是分维考虑变成一维问题,每次先统一往上再统一往下。
然后发现这样移动完起始点和目标点之后,每一对点的纵坐标都是一样的,不同对的纵坐标不一样,直接移动不会交叉。
CF 1320F
我们考虑一开始填满整个格子然后调整:如果某一个传感器报告没有看到任何格子就删除它能看到的那个格子。
如果某个格子对着的两个传感器是矛盾的也删除。
然后模拟就做完了。
CF 607E
二分答案,相当于问以某个点为圆心,$r$ 为半径的圆内有多少个交点。
发现如果直线和这个询问有关系,首先这个直线肯定在圆上有两个不同的交点,我们把这些交点求出来。
把所有点按照极角排序,设直线 $i$ 的两个交点的编号是 $l_i,r_i$ 。我们发现两条直线 $x,y$ 在圆内相交当且仅当 $l_x \leq l_y \leq r_x \leq r_y$。先考虑怎么对这个计数:这是个经典的扫描线问题,对于这样的一对 $(x,y)$,我们在 $l_y$ 的地方统计,所以当扫到 $l_x$ 的时候就将 $r_x$ 单点加,扫到 $l_y$ 的时候查询区间 $l_y,r_y$,扫到 $r_x$ 的时候删掉。
查询方案,注意到 $m \leq 3 \times 10^7$,所以可以用 set 维护这个操作,每次二分出来位置都输出就行了。注意我们要先处理距离 $<ans$ 的再处理 $=ans$ 的,因为可能有多个 $=ans$。
CF 538H
设 $mnr = \min_{i=1}^n r_i,mxl = \max_{i=1}^n l_i$。我们考虑先确定 $n_1,n_2$ ,分类讨论一下:
如果 $mnr \geq mxl$,也就是所有线段有交,如果 $mnl+mxr \not \in [t,T]$,那么我们需要进行调整,我们发现 $mxl$ 一定只会增加,$mnr$ 一定只会减少。移动了这两个位置相当于是缩紧了限制,这样会让限制尽量送。
如果 $mnr < mxl$,也就是有一对线段无交,那么$mnr$ 只能减少,$mxl$ 只能增加,要不然会有线段无法选到。
那么根据上述策略确定了 $n_1,n_2$,接下来是确定方案。
按照冲突关系建图,每个连通块考虑。首先每个连通块必须是二分图,然后就可以枚举一下 $n_1$ 对应白点还是黑点即可。
CF 1120F
注意观察题目的性质:
- 如果某个时刻有信寄存了,那么接下来的每个时刻信箱里都会有信。
- 如果某个时刻有信寄存了,那么接下来的每个连续段的开头都会去寄信。首先另一个人一定要去寄信,那么一定是越早越优
所以我们倒着枚举第一次寄信的时间,每次相当于前面的都直接寄信,后面的如果不是连续段开头就可以自由选择最优解(因为每个信被取出的时间都知道了,就是后面第一个和这个不同的点之间的距离),如果是连续段开头就只能选择寄信。
CF 1310C
二分答案,这里可以通过 $O(n^2)$ 预处理每对的 $\text{lcp}$ 然后直接对子串排序,这样复杂度就是正确的 $O(n^2 \log n)$。
然后我们考虑查询比它字典序大的串有哪些,等价于要保证每一段的字典序都 $\geq $ 当前串。
设 $f_{i,j}$ 表示考虑 $[1\ldots i]$,划分了 $j$ 段的方案数,枚举上一个划分的位置,相当于要比较字典序,需要比较 $lcp_j$ 和 $lcp_x$。。。哎这个 $j$ 怎么是变的这咋维护啊。。。
如果求了 $\text{lcp}$ 然后要 dp 的话对着后缀 dp 就好了。
所以我们把状态改成考虑了 $[i\ldots n]$,划分了 $j$ 段的方案数,这样就能求出当前后缀和二分的答案的 $\text{lcp}$,分情况讨论:
- $lcp$ 长度 $<$ 二分的串的长度:最后一次分割出来的串要满足长度 $> lcp$ 并且必须要在第一个不同的位置字典序严格大于二分的串
- $lcp$ 长度 $\geq$ 二分的串的长度:只需要保证长度 $\geq lcp$ 即可。
分类转移即可。注意由于方案数过大,需要设置一个 $\infty$ 防止爆long long。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 1000+5;
const LL INF = 4e18;
int n,m;LL k;
char str[MAXN];
int lcp[MAXN][MAXN];
std::vector<P> S;
inline bool cmp(P a,P b){
int lcp = ::lcp[a.fi][b.fi];
if(a.fi+lcp > a.se || b.fi+lcp > b.se) return a.se-a.fi < b.se-b.fi;
return str[a.fi+lcp] < str[b.fi+lcp];
}
LL f[MAXN][MAXN];
// lcp的东西就应该考虑从后向前转移。。
inline bool chk(int l,int r){
CLR(f,0);
f[0][n+1] = 1;int len = r-l+1;
FOR(i,1,m){
ROF(j,n,0){
f[i-1][j] += f[i-1][j+1];
if(f[i-1][j] >= INF) f[i-1][j] = INF;
}
ROF(j,n-i+1,1){
int lcp = ::lcp[l][j];
if(lcp < r-l+1){
if(str[j+lcp] > str[l+lcp]){
f[i][j] += f[i-1][j+lcp+1];
if(f[i][j] >= INF) f[i][j] = INF;
}
}
else{
lcp = std::min(lcp,r-l+1);
f[i][j] += f[i-1][j+lcp];
if(f[i][j] >= INF) f[i][j] = INF;
}
}
}
return f[m][1] >= k;
}
int main(){
scanf("%d%d%lld",&n,&m,&k);
scanf("%s",str+1);
ROF(i,n,1){
ROF(j,n,1){
if(str[i] == str[j]) lcp[i][j] = lcp[i+1][j+1]+1;
else lcp[i][j] = 0;
}
}
FOR(i,1,n) FOR(j,i,n) S.pb(MP(i,j));
std::sort(all(S),cmp);
int l = 0,r = (int)S.size()-1,ans = -1;
while(l <= r){
int mid = (l + r) >> 1;
if(chk(S[mid].fi,S[mid].se)) ans = mid,l = mid+1;
else r = mid-1;
}
FOR(i,S[ans].fi,S[ans].se) putchar(str[i]);puts("");
return 0;
}
UOJ305
分数规划,二分答案 $\geq x$,相当于判定是否存在一个环,满足 $\text{收益}-\text{边权}x \geq 0$。
首先我们预处理出 $g_{i,j}$ 表示从 $i$ 走到 $j$ ,并且从 $i$ 买从 $j$ 卖的最大代价,然后相当于就是一个有向图找最大环问题了,这里梳理一下:
无向图找最大环:Floyd 同时枚举每个点作为环终点,更新是 ans = max(ans,f[i][j]+e[j][k]+e[k][i])
;
有向图找最大环:初始矩阵全体设为负无穷,跑最短路,用 f[i][i]
更新答案。