一些题
一些题
模拟赛遇到的trick,有意思的,有启发的题,不一定很难。
蜀道难
噫吁嚱,危乎高哉!蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!尔来四万八千岁,不与秦塞通人烟。西当太白有鸟道,可以横绝峨眉巅。地崩山摧壮士死,然后天梯石栈相钩连。上有六龙回日之高标,下有冲波逆折之回川。黄鹤之飞尚不得过,猿猱欲度愁攀援。青泥何盘盘,百步九折萦岩峦。扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。但见悲鸟号古木,雄飞雌从绕林间。又闻子规啼夜月,愁空山。蜀道之难,难于上青天,使人听此凋朱颜!连峰去天不盈尺,枯松倒挂倚绝壁。飞湍瀑流争喧豗,砯崖转石万壑雷。其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。所守或匪亲,化为狼与豺。朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。锦城虽云乐,不如早还家。蜀道之难,难于上青天,侧身西望长咨嗟!
连通块
有一棵 \(n\) 个节点的树,他现在要对这棵树做 \(m\) 次操作,每次操作如下
-
删除树上的一条边
-
查询在节点 \(u\) 目前所在的连通块内,离 \(u\) 最远的点的距离。两点之间的距离定义为两点之间简单路径的边数。
树的直径的性质
-
直径的端点一定是树的叶子节点。
-
从树上任意一节点出发,与它距离最远的节点一定是直径的一个端点。
-
在树上增加一个叶子节点,最多改变树的直径的一个端点。
-
设两棵树的直径端点分别为 $ (u,v) 和 (x,y) $ ,将两棵树合并后的直径端点仍在这四点中。
-
如果一棵树有多条直径,它们一定交与一个节点,且这个交点是每条直径的中点。
考虑求树的直径:
-
贪心。由性质2,任由一点开始求最远点,两边DFS即可。
-
DP。求该子树中的最长链和次长链。
再来看这道题,我们把操作离线,反向操作,就成为合并子树问题。由性质2可知,我们只需要知道直径端点就能求出最大距离。再由性质4,很容易维护树的直径。
CODE
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
int n,m,a,b,cnt,head[N],fa[N];
bool p[N],vis[N],vs[N];
int dad[N],siz[N],depth[N],son[N],top[N];
int fir[N],sec[N],maxn[N],maxx[N],ans[N];
struct node{
int x,y,dis;
};
struct edge{
int to,nxt;
}e[N<<1];
struct query{
int opt,x;
}q[N];
bool cmp(node i,node j){
return i.dis>j.dis;
}
inline void add(int u,int v){
cnt++;
e[cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int find(int x){
if(x==fa[x])return x;
else return fa[x]=find(fa[x]);
}
void dfs(int u,int f){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f)continue;
if(!p[i>>1]){
int fx=find(u),fy=find(v);
if(fx!=fy)fa[fx]=fy;
}
dfs(v,u);
}
}
void dfs1(int u,int f){
depth[u]=depth[f]+1;
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f)continue;
dad[v]=u;
dfs1(v,u);
if(siz[son[u]]<siz[v])son[u]=v;
siz[u]+=siz[v];
}
}
void dfs2(int u,int t){
top[u]=t;
if(son[u]){
dfs2(son[u],t);
}
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==dad[u]||v==son[u])continue;
dfs2(v,v);
}
}
int LCA(int x,int y){
int fx=top[x],fy=top[y];
while(fx!=fy){
if(depth[fx]<depth[fy]){
y=dad[fy];
}
else{
x=dad[fx];
}
fy=top[y];fx=top[x];
}
if(depth[x]<depth[y])return x;
else return y;
}
void dfs3(int rt,int u,int f){
fir[u]=u;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
if(p[i>>1])continue;
int v=e[i].to;
if(v==f)continue;
if(vis[v])continue;
dfs3(rt,v,u);
if(maxn[v]+1>maxn[u]){
maxn[u]=maxn[v]+1;
fir[u]=fir[v];
}
}
}
void dfs4(int rt,int u,int f){
sec[u]=u;
vs[u]=1;
for(int i=head[u];i;i=e[i].nxt){
if(p[i>>1])continue;
int v=e[i].to;
if(v==f)continue;
if(vs[v])continue;
dfs4(rt,v,u);
if(maxx[v]+1>maxx[u]){
maxx[u]=maxx[v]+1;
sec[u]=sec[v];
}
}
}
int Dis(int x,int y){
return depth[x]+depth[y]-depth[LCA(x,y)]*2;
}
signed main()
{
freopen("block.in","r",stdin);
freopen("block.out","w",stdout);
scanf("%d%d",&n,&m);
cnt=1;
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].opt,&q[i].x);
if(q[i].opt==1){
p[q[i].x]=1;
}
}
dfs(1,0);
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=n;i++){
if(!vis[i]){
int anc=find(i);
dfs3(anc,anc,0);
dfs4(anc,fir[anc],0);
sec[anc]=sec[fir[anc]];
}
}
for(int i=m;i>=1;i--){
if(q[i].opt==1){
int x=(e[q[i].x<<1].to),y=(e[q[i].x<<1|1].to);
int fx=find(x),fy=find(y);
node d[6];
d[0]={fir[fx],sec[fx],Dis(fir[fx],sec[fx])};
d[1]={fir[fx],fir[fy],Dis(fir[fx],fir[fy])};
d[2]={fir[fx],sec[fy],Dis(fir[fx],sec[fy])};
d[3]={sec[fx],fir[fy],Dis(sec[fx],fir[fy])};
d[4]={sec[fx],sec[fy],Dis(sec[fx],sec[fy])};
d[5]={fir[fy],sec[fy],Dis(fir[fy],sec[fy])};
int dis=0;
for(int j=0;j<6;j++){
if(dis<d[j].dis){
dis=d[j].dis;
fir[fy]=d[j].x;
sec[fy]=d[j].y;
}
}
fa[fx]=fy;
}
else{
int fx=find(q[i].x);
ans[i]=max(Dis(q[i].x,fir[fx]),Dis(q[i].x,sec[fx]));
}
}
for(int i=1;i<=m;i++){
if(q[i].opt==2)printf("%d\n",ans[i]);
}
}
Wallpaper Collection
设 $ dp_{i,j,k} $ 为第 \(i\) 行,所选区间为 $ [j,k] $ 最大价值,转移时用滚动数组和一些优化,就可做到 $ O(n^3) $ 。
改变DP状态:我们发现相邻两行所选区间必须有交集,想象一个学长在矩阵上走动,他走过的路径刚好符合以上定义。设 $ dp{i,j} $ 为学长在第 \(i\) 行,第一次走到的是第 \(j\) 格的最大价值,加一些优化就可以 $ O(n^2) $ 。
进击的巨人
一个 $ O(n^2) $ 的做法:对于每一个位置 \(i\) ,枚举 $ j \le i $ ,计算 $ [j,i] $ 都是1的概率,进而可以算出期望。因为赛时数据水,$ n\le 1e4 $ , $ O(n^2) $ 就跑过来了。
设 $ [L,R] $ 都是1,那么 $ [L-1,R] $ 都没有被损坏,防御值为 $ (R-L+1)^k $ 。
二项式定理展开得到:
记前 \(i\) 个位置?的个数为 \(s_i\) ,位置 \(i\) 的答案为:
发现这个东西极好维护,复杂度 $ O(NK) $ 。
魔卡少女樱
$ a_i \not\equiv a_{i+1} \pmod 3 $ 。差分的思路显然, $ a_{i+1}-a_i>0 \ \And a_{i+1}-a_i \not\equiv 0 \pmod 3 $ 。我们先考虑差分结果只有1,2的情况,枚举填 \(k\) 个2, $ n-1-k $ 个1,这里方案数为:设填2则 \(x_i\) 值为1,填1则 $ x_i $ 值为0,$ \sum x=k $ ,有 $ \dbinom{n+k-2}{n-2} $ 种。
当前 $ a_n= n-1+k $,对于剩下的 $ m-a_n $ 没有使用,我们可以填进 $ a_1 $ ,也可以三个一组加到差分数上(正确性显然)。
设填 \(k\) 个2,枚举 $ a_1 $,三个一组加,共有 $ t= \left \lfloor \frac{m-(n-1+k)-a_1} {3} \right \rfloor $ 组 。方案数为 $ \sum_{i=0}^{t} \dbinom{ n+i-2 }{ n-2 } $(赛时因为这个挂分不止一次了)
最终答案
枚举就得到 $ O(n^3) $ 做法,加一堆前缀和优化就做到 $ O(n) $
函数
设 $ f_1(x)=A_1x+B_1 ,f_2(x)=A_2x+B_2 $ 。考虑何时 $ f_1(f_2(x))>f_2(f_1(x)) $
按 $ \frac{B_i}{A_i-1} $ 排序,DP即可。
中国军魂
如果祖国遭受到侵犯
热血男儿当自强
喝干这碗家乡的酒
壮士一去不复返
滚滚黄河 滔滔长江
给我生命 给我力量
就让鲜血染红最美的花
洒在我的胸膛上
红旗飘飘 军号响
剑已出鞘 雷鸣电闪
从来是狭路相逢勇者胜
向前进 向前进
向前进 向前进
红旗飘飘 军号响
剑已出鞘 雷鸣电闪
从来是狭路相逢勇者胜
向前进 向前进
向前进 向前进
向前进
中国军魂
如果祖国遭受到侵犯
热血男儿当自强
喝干这碗家乡的酒
壮士一去不复返
滚滚黄河 滔滔长江
给我生命 给我力量
就让鲜血染红最美的花
洒在我的胸膛上
红旗飘飘 军号响
剑已出鞘 雷鸣电闪
从来是狭路相逢勇者胜
向前进 向前进
向前进 向前进
红旗飘飘 军号响
剑已出鞘 雷鸣电闪
从来是狭路相逢勇者胜
向前进 向前进
向前进 向前进
向前进
中国军魂
博弈
一个显然结论,对于点对 $ (u,v) $ ,Crying有必胜策略当且仅当路径上某一权值出现次数为奇数。换言之,如果所有权值出现次数都是偶数就没有必胜策略。考虑异或和,这样如果出现次数为偶数,异或和就为0,但是有问题,比如 $ 1,2,3 $ 异或和为0,会误判。考虑随机化解决问题(或者哈希也可以),我们随机一个边权,避免被卡掉,跑 dsu on tree。记得开 long long,int会被卡。
金箱子
比较简单,二项式定理展开。
$ dp[i][j] $ 为前 \(i\) 位,缴纳税款为 $ x^j $ 的期望,有
可持久化字符串
终于深刻理解了AC自动机。
把所有操作离线,建AC自动机,这里fail指针指的是当前字符串的最长后缀。把B字符串在AC自动机上跑,处理每个字符串被匹配次数,这里如果几个字符串存在后缀关系,匹配次数都会累加到最长的能匹配上的串,所以我们要用失配树跑拓扑,把儿子的价值加到父亲上,这样才是真正的每个字符串的匹配次数,处理前缀和。
特判根节点(空串),因为空串认为只匹配一次。另外输出答案时n和m不要看反(似曾相识燕归来)
Non-breath oblige
注意到每一个查询操作能确定最后一个修改它的2操作(或者为空),之前的操作都被覆盖了,没有影响,思索怎么求最后一次修改。每个1操作会改变两个点,2操作会改变一个区间,发现线段树可做。因为每次序列都会重置,如果最后修改不在区间内,相当于为空,这样就成为二维数点问题。(似乎有点卡常)
跳跃
把序列划分为若干区间使得每个区间价值和达到能达到的最大,发现我们总是在几个区间反复横跳,当k足够大时最终到达一个和最大的区间,然后狂跳不止。不知道怎么看出来的,反正我赛时没注意。应该不难理解。
考虑一个诡异的DP来计算到达每个点需要的最小步数(价值不能出现负的),步数相同的前提下求价值最大,这样决策的正确性可以想想。另外发现我们所选的区间位置一定是单调递增的,可以反证,对于当前区间 \([L,R]\) 如果存在区间 $ [L_1,R_1] $ 和 $ [L_2,R_2] $ ,其中 $ L \le R \le L_1 \le R_1 \le L_2 \le R_2 $ ,满足由 $ [L,R] $ 可以跳到 $ [L_2,R_2] $ ,但不能跳到 $ [L_1,R_1] $ ,那么 $ \sum_{i = R_1+1}^{R_2} a_i > 0 $ ,进而 $ \sum_{ i = L_1 }^{ R_2 } a_i > \sum_{ i = L_1 }^{ R_1 } a_i $ ,$ [L_1,R_1] [L_2,R_2] $ 应当合并。
所以我们只要单向转移DP即可,复杂度 $ O(n^2) $
法阵
大mo法师小C有一个法阵,由一个 \(n\) 行 \(m\) 列的方阵构成,在排布法阵时,ta会在这些格子中放置一些水晶球
然而,由于水晶球奇特的魔法特性
- 一行既不能不放水晶球,也不能出现两个不相邻的水晶球
- 一列既不能全放满水晶球,也不能有两个不相邻的空位
为了避免出现临时法阵不足的情况,ta决定让你来求出这样不同的法阵个数
这是好题,但是实在不知道怎么写题解。其实就是分讨可能的合法情况,DP统计方案。大概就是分为:水晶球是两个梯形(上下),空白是两个梯形(左右),还有一种都是梯形的情况。一坨前缀和优化就做到了 $ O(n^2) $
《阿房宫赋》
六王毕,四海一;蜀山兀,阿房出。覆压三百余里,隔离天日。骊山北构而西折,直走咸阳。二川溶溶,流入宫墙。五步一楼,十步一阁;廊腰缦回,檐牙高啄;各抱地势,钩心斗角。盘盘焉,囷囷焉,蜂房水涡,矗不知其几千万落!长桥卧波,未云何龙?复道行空,不霁何虹?高低冥迷,不知西东。歌台暖响,春光融融;舞殿冷袖,风雨凄凄。一日之内,一宫之间,而气候不齐。
妃嫔媵嫱,王子皇孙,辞楼下殿,辇来于秦,朝歌夜弦,为秦宫人。明星荧荧,开妆镜也;绿云扰扰,梳晓鬟也;渭流涨腻,弃脂水也;烟斜雾横,焚椒兰也。雷霆乍惊,宫车过也;辘辘远听,杳不知其所之也。一肌一容,尽态极妍,缦立远视,而望幸焉;有不见者,三十六年。燕、赵之收藏,韩、魏之经营,齐、楚之精英,几世几年,剽掠其人,倚叠如山。一旦不能有,输来其间。鼎铛玉石,金块珠砾,弃掷逦迤,秦人视之,亦不甚惜。
嗟乎!一人之心,千万人之心也。秦爱纷奢,人亦念其家;奈何取之尽锱铢,用之如泥沙?使负栋之柱,多于南亩之农夫;架梁之椽,多于机上之工女;钉头磷磷,多于在庾之粟粒;瓦缝参差,多于周身之帛缕;直栏横槛,多于九土之城郭;管弦呕哑,多于市人之言语。使天下之人,不敢言而敢怒;独夫之心,日益骄固。戍卒叫,函谷举;楚人一炬,可怜焦土。
呜呼!灭六国者,六国也,非秦也。族秦者,秦也,非天下也。嗟乎!使六国各爱其人,则足以拒秦;使秦复爱六国之人,则递三世可至万世而为君,谁得而族灭也?秦人不暇自哀,而后人哀之;后人哀之而不鉴之,亦使后人而复哀后人也。