noip2018
ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强
D1T1
春春是一名道路工程师,负责铺设一条长度为 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 块首尾相连的区域,一开始,第 块区域下陷的深度为 。
春春每天可以选择一段连续区间 ,填充这段区间中的每块区域,让其下陷深度减少 。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 。
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 。
题解
利用差分思想,用得到新的序列,然后
效率
D1T2
在网友的国度中共有 种不同面额的货币,第 种货币的面额为 ,你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 、面额数组为 的货币系统记作 。
在一个完善的货币系统中,每一个非负整数的金额 都应该可以被表示出,即对每一个非负整数 ,都存在 个非负整数 满足 的和为 。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 不能被该货币系统表示出。例如在货币系统 , 中,金额 就无法被表示出来。
两个货币系统 和 是等价的,当且仅当对于任意非负整数 ,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。
现在网友们打算简化一下货币系统。他们希望找到一个货币系统 ,满足 与原来的货币系统 等价,且 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 。
题解
显然 新的货币系统的 的每个数都在原来的 中出现
证明一下?
如果说有新的数出现在 中,那么它要么可以被原来的数表示,是不优的,要么不能被原来的数表示,是不符合条件的
所以问题转化成 中留下的数使得不留下的数都被留下的数表示,且最小化留下的数的个数
所以我们设 表示用 中的数组成 这个数的方案数
显然有
最后如果 ,那么它就只能被自身表示,故
效率
D1T3
C 城将要举办一系列的赛车比赛。在比赛前,需要在城内修建 条赛道。
C 城一共有 个路口,这些路口编号为 ,有 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口。其中,第 条道路连接的两个路口编号为 和 ,该道路的长度为 。借助这 条道路,从任何一个路口出发都能到达其他所有的路口。
一条赛道是一组互不相同的道路 ,满足可以从某个路口出发,依次经过道路 (每条道路经过一次,不允许调头)到达另一个路口。一条赛道的长度等于经过的各道路的长度之和。为保证安全,要求每条道路至多被一条赛道经过。
目前赛道修建的方案尚未确定。你的任务是设计一种赛道修建的方案,使得修建的 条赛道中长度最小的赛道长度最大(即 条赛道中最短赛道的长度尽可能大)
题解
通过部分分可以大致推出正解算法
首先当 时,显然是树的直径最优
然后当 号点是根节点,可以利用贪心的思想,让一些边权大的边单独最为赛道,然后剩下的就小的和大的顺序匹配,取 即为答案
当一条链时,显然可以二分答案
所以这题正解应该往二分+贪心想
首先我们二分答案 ,然后我们希望儿子传给父亲的边尽量大,所以我们可以把每个点得到的边权从小到大依次匹配到一条边,使得这两条边相加不小于 ,然后把剩余的最大的边往上传递即可,这里可以用 维护边权
效率 ,其中 为二分的右端点
D2T1
小 是一个爱好旅行的 。她来到X国,打算将各个城市都玩一遍。
小 了解到,国的 个城市之间有 条双向道路。每条双向道路连接两个城市。不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且,从任意一个城市出发通过这些道路都可以到达任意一个其他城市。小 只能通过这些道路从一个城市前往另一个城市。
小 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该城市时经过的道路后退到上一个城市。当小 回到起点时,她可以选择结束这次旅行或继续旅行。需要注意的是,小 要求在旅行方案中,每个城市都被访问到。
为了让自己的旅行更有意义,小 决定在每到达一个新的城市(包括起点)时,将它的编号记录下来。她知道这样会形成一个长度为 的序列。她希望这个序列的字典序最小,你能帮帮她吗? 对于两个长度均为 的序列 和 ,当且仅当存在一个正整数 时,满足以下条件时,我们说序列 的字典序小于 。
对于任意正整数 ,序列 的第 个元素 和序列 的第 个元素 相同。
序列 的第 个元素的值小于序列 的第 个元素的值。
题解
因为 或 ,所以我们可以分类一下
当 时,我们直接从 号点 即可,然后每次编号小的先走即可
当 时,可以发现最后只有环上的一条边走不到,所以枚举删去环上的一条边,然后取最优的走法即可
效率
D2T2
小 特别喜欢玩游戏。这一天,他在玩一款填数游戏。
这个填数游戏的棋盘是一个 的矩形表格。玩家需要在表格的每个格子中填入一个数字(数字 或者数字 ),填数时需要满足一些限制。
下面我们来具体描述这些限制。
为了方便描述,我们先给出一些定义:
• 我们用每个格子的行列坐标来表示一个格子,即(行坐标,列坐标)。(注意:行列坐标均从 开始编号)
• 合法路径 :一条路径是合法的当且仅当:
- 这条路径从矩形表格的左上角的格子 出发,到矩形的右下角格子 结束;
- 在这条路径中,每次只能从当前的格子移动到右边与它相邻的格子,或者从当前格子移动到下面与它相邻的格子。
对于一条合法的路径 ,我们可以用一个字符串 来表示,该字符串的长度为 ,其中只包含字符 或者字符 ,第 个字符记录了路径 中第 步的移动方法, 表示移动到当前格子右边与它相邻的格子, 表示移动到当前格子下面与它相邻的格子。
同时,将每条合法路径 经过的每个格子上填入的数字依次连接后,会得到一个长度为 的 字符串,记为 。
游戏要求小 找到一种填数字 的方法,使得对于两条路径,,如果 ,那么必须 。我们说字符串 字符串 ,当且仅当字符串 的字典序小于字符串 的字典序,字典序的定义详见第一题。但是仅仅是找一种方法无法满足小 的好奇心,小 更想知道这个游戏有多少种玩法,也就是说,有多少种填数字的方法满足游戏的要求?
小 能力有限,希望你帮助他解决这个问题,即有多少种填 的方法能满足题目要求。由于答案可能很大,你需要输出答案对 取模的结果。
题解
通过打表可以找出规律,然后就可以过了
效率
D2T3
国有 座城市, 条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。
国的国防部长小 要在城市中驻扎军队。驻扎军队需要满足如下几个条件:
● 一座城市可以驻扎一支军队,也可以不驻扎军队。
● 由道路直接连接的两座城市中至少要有一座城市驻扎军队。
● 在城市里驻扎军队会产生花费,在编号为 的城市中驻扎军队的花费是 。
小 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 提出了 个要求,每个要求规定了其中两座城市是否驻扎军队。小 需要针对每个要求逐一给出回答。具体而言,如果国王提出的第 个要求能够满足上述驻扎条件(不需要考虑第 个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第 个要求无法满足,则需要输出 。现在请你来帮助小 。
题解
似乎考场大家都会写 的暴力 ,只有我不会嘤嘤嘤
发现暴力不优秀的地方在于每次用 的效率更新了所有的 值,但是仔细想想有一些值是不用被更改的,所以我们要优化这一部分
设 表示选/不选第 个点,在第 棵子树内的最小代价
所以若没有限制,可以得到方程式
对于一个询问 ,我们可以一开始强制让不能出现的情况设为 ,然后我们发现我们会修改 的路径有 , , 这三条链,其他的子树的答案保持不变
考虑倍增预处理
设 表示选/不选 ,选/不选 向上走 步的祖先 时,从 的不包括 这个点的子树的答案(即 这个子树的答案扣除 这个子树的答案)
转移很简单
然后我们可以直接把 到 的路径上的 数组加起来,并加上 的子树和 的子树的 值,再从 开始加到根节点,就能算出总费用啦
问题来了, 值都是包含当前我们在做的链下面的子树的贡献的,比如我们要用新的 的 值更新出新的 的父亲 的 值时,要计算 的别的子树的贡献,可是怎么处理出这一部分答案呢?
显然别的子树的答案是不会变的,而只有 这个子树的 值才会更改,所以我们可以先把这一部分在预处理时给 的贡献值扣去,再加上新的值
所以我们可以得到
表示要新加的值
还有一些细节,具体看代码吧
#include <bits/stdc++.h>
#define F 2e18
#define I inline
#define LL long long
#define _(d) while(d(isdigit(c=getchar())))
using namespace std;
const int N=1e5+5;char typ[2];
I int R(){
int x;bool f=1;char c;_(!) if (c==45) f=0;x=c^48;
_() x=(x<<3)+(x<<1)+(c^48);return f?x:-x;
}
int n,m,head[N],V[N*2],nex[N*2],t,dep[N],fa[20][N],L;
LL g[2][N],f[2][2][20][N],a[N],X[2],Y[2],h[2],l[2],ans;
I void add(int u,int v){V[++t]=v;nex[t]=head[u];head[u]=t;}
void dfs1(int x){
dep[x]=dep[fa[0][x]]+1;
g[1][x]=a[x];f[0][0][0][x]=F;
for (int i=1;fa[i-1][fa[i-1][x]];i++)
fa[i][x]=fa[i-1][fa[i-1][x]];
for (int i=head[x];i;i=nex[i]){
if (V[i]==fa[0][x]) continue;
fa[0][V[i]]=x;dfs1(V[i]);
g[0][x]+=g[1][V[i]];
g[1][x]+=min(g[0][V[i]],g[1][V[i]]);
}
}
void dfs2(int x){
f[1][0][0][x]=g[0][fa[0][x]]-g[1][x];
f[0][1][0][x]=f[1][1][0][x]=g[1][fa[0][x]]-min(g[0][x],g[1][x]);
for (int i=1;fa[i][x];i++) for (int j=0;j<2;j++) for (int k=0;k<2;k++)
f[j][k][i][x]=min(f[j][0][i-1][x]+f[0][k][i-1][fa[i-1][x]],f[j][1][i-1][x]+f[1][k][i-1][fa[i-1][x]]);
for (int i=head[x];i;i=nex[i]) if (V[i]!=fa[0][x]) dfs2(V[i]);
}
I void work(int x,int tx,int y,int ty){
X[0]=X[1]=Y[0]=Y[1]=l[0]=l[1]=F;
X[tx]=g[tx][x];Y[ty]=g[ty][y];
int r=dep[x]-dep[y];
for (int j=19;~j;j--) if ((1<<j)&r){
h[0]=X[0];h[1]=X[1];
X[0]=min(h[0]+f[0][0][j][x],h[1]+f[1][0][j][x]);
X[1]=min(h[0]+f[0][1][j][x],h[1]+f[1][1][j][x]);
x=fa[j][x];
}
if (x==y) L=y,l[ty]=X[ty];
else{
for (int j=19;~j;j--) if(fa[j][x]!=fa[j][y]){
h[0]=X[0];h[1]=X[1];
X[0]=min(h[0]+f[0][0][j][x],h[1]+f[1][0][j][x]);
X[1]=min(h[0]+f[0][1][j][x],h[1]+f[1][1][j][x]);
h[0]=Y[0];h[1]=Y[1];
Y[0]=min(h[0]+f[0][0][j][y],h[1]+f[1][0][j][y]);
Y[1]=min(h[0]+f[0][1][j][y],h[1]+f[1][1][j][y]);
x=fa[j][x];y=fa[j][y];
}
L=fa[0][x];l[0]=g[0][L]-g[1][x]-g[1][y]+X[1]+Y[1];
l[1]=g[1][L]-min(g[0][x],g[1][x])-min(g[0][y],g[1][y])+min(X[0],X[1])+min(Y[0],Y[1]);
}
if (L==1) ans=min(l[0],l[1]);
else{
r=dep[L]-2;
for (int j=19;~j;j--) if((1<<j)&r){
h[0]=l[0];h[1]=l[1];
l[0]=min(h[0]+f[0][0][j][L],h[1]+f[1][0][j][L]);
l[1]=min(h[0]+f[0][1][j][L],h[1]+f[1][1][j][L]);
L=fa[j][L];
}
ans=min(g[0][1]-g[1][L]+l[1],g[1][1]-min(g[0][L],g[1][L])+min(l[0],l[1]));
}
}
int main(){
n=R();m=R();scanf("%s",typ);
for (int i=1;i<=n;i++) a[i]=R();
for (int x,y,i=1;i<n;i++)
x=R(),y=R(),add(x,y),add(y,x);
dfs1(1);dfs2(1);
for (int x,tx,y,ty;m--;){
x=R();tx=R();y=R();ty=R();
if (dep[x]<dep[y]) swap(x,y),swap(tx,ty);
work(x,tx,y,ty);printf("%lld\n",ans<F?ans:-1);
}
return 0;
}