noip2018

ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强ccj好强

D1T1

春春是一名道路工程师,负责铺设一条长度为 nn 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 nn 块首尾相连的区域,一开始,第 ii 块区域下陷的深度为 did_i
春春每天可以选择一段连续区间 [L,R][L,R] ,填充这段区间中的每块区域,让其下陷深度减少 11。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 00
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 00

题解

利用差分思想,用didi1d_i-d_{i-1}得到新的序列aia_i,然后ans=max(ai,0)ans=max(a_i,0)
效率 O(n)O(n)

D1T2

在网友的国度中共有 nn 种不同面额的货币,第 ii 种货币的面额为 a[i]a[i],你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 nn、面额数组为 a[1..n]a[1..n] 的货币系统记作 (n,a)(n,a)
在一个完善的货币系统中,每一个非负整数的金额 xx 都应该可以被表示出,即对每一个非负整数 xx,都存在 nn 个非负整数 t[i]t[i] 满足 a[i]×t[i]a[i] \times t[i] 的和为 xx。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 xx 不能被该货币系统表示出。例如在货币系统 n=3n=3, a=[2,5,9]a=[2,5,9] 中,金额 1,31,3 就无法被表示出来。
两个货币系统 (n,a)(n,a)(m,b)(m,b) 是等价的,当且仅当对于任意非负整数 xx,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。
现在网友们打算简化一下货币系统。他们希望找到一个货币系统 (m,b)(m,b),满足 (m,b)(m,b) 与原来的货币系统 (n,a)(n,a) 等价,且 mm 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 mm

题解

显然 新的货币系统的 bib_i 的每个数都在原来的 aia_i 中出现
证明一下?
如果说有新的数出现在 bib_i 中,那么它要么可以被原来的数表示,是不优的,要么不能被原来的数表示,是不符合条件的
所以问题转化成 aia_i 中留下的数使得不留下的数都被留下的数表示,且最小化留下的数的个数
所以我们设 fjf_j 表示用 aia_i 中的数组成 jj 这个数的方案数
显然有 fj+=fjaif_j+=f_{j-a_i}
最后如果 fai=1f_{a_i}=1,那么它就只能被自身表示,故 ans++ans++
效率 O(T×n×amax)O(T \times n \times a_{max})

D1T3

C 城将要举办一系列的赛车比赛。在比赛前,需要在城内修建 mm 条赛道。
C 城一共有 nn 个路口,这些路口编号为 1,2,,n1,2,…,n,有 n1n-1 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口。其中,第 ii 条道路连接的两个路口编号为 aia_ibib_i,该道路的长度为 lil_i 。借助这 n1n-1 条道路,从任何一个路口出发都能到达其他所有的路口。
一条赛道是一组互不相同的道路 e1,e2,,eke_1,e_2,…,e_k ,满足可以从某个路口出发,依次经过道路 e1,e2,,eke_1,e_2,…,e_k (每条道路经过一次,不允许调头)到达另一个路口。一条赛道的长度等于经过的各道路的长度之和。为保证安全,要求每条道路至多被一条赛道经过。
目前赛道修建的方案尚未确定。你的任务是设计一种赛道修建的方案,使得修建的 mm 条赛道中长度最小的赛道长度最大(即 mm 条赛道中最短赛道的长度尽可能大)

题解

通过部分分可以大致推出正解算法
首先当 m=1m=1 时,显然是树的直径最优
然后当 11 号点是根节点,可以利用贪心的思想,让一些边权大的边单独最为赛道,然后剩下的就小的和大的顺序匹配,取 minmin 即为答案
当一条链时,显然可以二分答案
所以这题正解应该往二分+贪心想
首先我们二分答案 midmid,然后我们希望儿子传给父亲的边尽量大,所以我们可以把每个点得到的边权从小到大依次匹配到一条边,使得这两条边相加不小于 midmid,然后把剩余的最大的边往上传递即可,这里可以用 setset 维护边权
效率 O(n×logn×logr)O(n \times logn \times logr),其中 rr 为二分的右端点

D2T1

YY 是一个爱好旅行的 OIerOIer。她来到X国,打算将各个城市都玩一遍。
YY 了解到,XX国的 nn 个城市之间有 mm 条双向道路。每条双向道路连接两个城市。不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且,从任意一个城市出发通过这些道路都可以到达任意一个其他城市。小 YY 只能通过这些道路从一个城市前往另一个城市。
YY 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该城市时经过的道路后退到上一个城市。当小 YY 回到起点时,她可以选择结束这次旅行或继续旅行。需要注意的是,小 YY 要求在旅行方案中,每个城市都被访问到。
为了让自己的旅行更有意义,小 YY 决定在每到达一个新的城市(包括起点)时,将它的编号记录下来。她知道这样会形成一个长度为 nn 的序列。她希望这个序列的字典序最小,你能帮帮她吗? 对于两个长度均为 nn 的序列 AABB,当且仅当存在一个正整数 xx 时,满足以下条件时,我们说序列 AA 的字典序小于 BB
对于任意正整数 1i<x1≤i<x,序列 AA 的第 ii 个元素 AiA_i 和序列 BB 的第 ii 个元素 BiB_i 相同。
序列 AA 的第 xx 个元素的值小于序列 BB 的第 xx 个元素的值。

题解

因为 m=n1m=n-1m=nm=n,所以我们可以分类一下
m=n1m=n-1 时,我们直接从 11 号点 dfsdfs 即可,然后每次编号小的先走即可
m=nm=n 时,可以发现最后只有环上的一条边走不到,所以枚举删去环上的一条边,然后取最优的走法即可
效率 O(n2×logn)O(n^2 \times logn)

D2T2

DD 特别喜欢玩游戏。这一天,他在玩一款填数游戏。
这个填数游戏的棋盘是一个 n×mn×m 的矩形表格。玩家需要在表格的每个格子中填入一个数字(数字 00 或者数字 11 ),填数时需要满足一些限制。
下面我们来具体描述这些限制。
为了方便描述,我们先给出一些定义:
• 我们用每个格子的行列坐标来表示一个格子,即(行坐标,列坐标)。(注意:行列坐标均从 00 开始编号)
• 合法路径 PP:一条路径是合法的当且仅当:

  1. 这条路径从矩形表格的左上角的格子 (0,0)(0,0) 出发,到矩形的右下角格子 (n1,m1)(n−1,m−1) 结束;
  2. 在这条路径中,每次只能从当前的格子移动到右边与它相邻的格子,或者从当前格子移动到下面与它相邻的格子。

对于一条合法的路径 PP,我们可以用一个字符串 w(P)w(P) 来表示,该字符串的长度为 n+m2n+m−2,其中只包含字符 "R""R" 或者字符 "D""D",第 ii 个字符记录了路径 PP 中第 ii 步的移动方法,"R""R" 表示移动到当前格子右边与它相邻的格子,"D""D" 表示移动到当前格子下面与它相邻的格子。
同时,将每条合法路径 PP 经过的每个格子上填入的数字依次连接后,会得到一个长度为 n+m1n+m−10101 字符串,记为 s(P)s(P)
游戏要求小 DD 找到一种填数字 010、1 的方法,使得对于两条路径P1P1P2P2,如果 w(P1)>w(P2)w(P1)>w(P2),那么必须 s(P1)s(P2)s(P1)≤s(P2)。我们说字符串 aa 字符串 bb,当且仅当字符串 aa 的字典序小于字符串 bb 的字典序,字典序的定义详见第一题。但是仅仅是找一种方法无法满足小 DD 的好奇心,小 DD 更想知道这个游戏有多少种玩法,也就是说,有多少种填数字的方法满足游戏的要求?
DD 能力有限,希望你帮助他解决这个问题,即有多少种填 010、1 的方法能满足题目要求。由于答案可能很大,你需要输出答案对 109+710^9+7 取模的结果。

题解

通过打表可以找出规律,然后就可以过了
效率 O(logm)O(logm)

D2T3

ZZ 国有 nn 座城市, n1n−1 条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。
ZZ 国的国防部长小 ZZ 要在城市中驻扎军队。驻扎军队需要满足如下几个条件:
● 一座城市可以驻扎一支军队,也可以不驻扎军队。
● 由道路直接连接的两座城市中至少要有一座城市驻扎军队。
● 在城市里驻扎军队会产生花费,在编号为 ii 的城市中驻扎军队的花费是 pip_i
ZZ 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 ZZ 提出了 mm 个要求,每个要求规定了其中两座城市是否驻扎军队。小 ZZ 需要针对每个要求逐一给出回答。具体而言,如果国王提出的第 jj 个要求能够满足上述驻扎条件(不需要考虑第 jj 个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第 j(1jm)j(1≤j≤m)个要求无法满足,则需要输出 1−1。现在请你来帮助小 ZZ

题解

似乎考场大家都会写 O(n×m)O(n \times m) 的暴力 dpdp ,只有我不会嘤嘤嘤
发现暴力不优秀的地方在于每次用 O(n)O(n) 的效率更新了所有的 dpdp 值,但是仔细想想有一些值是不用被更改的,所以我们要优化这一部分
g0/1,ig_{0/1,i} 表示选/不选第 ii 个点,在第 ii 棵子树内的最小代价
所以若没有限制,可以得到方程式
g0,i=g1,vg_{0,i}=\sum g_{1,v}
g1,i=min(g0,v,g1,v)g_{1,i}=\sum min(g_{0,v},g_{1,v})
对于一个询问 (x,y)(x,y),我们可以一开始强制让不能出现的情况设为 INFINF ,然后我们发现我们会修改 gg 的路径有 u>lcau->lcav>lcav->lcalca>rootlca->root 这三条链,其他的子树的答案保持不变
考虑倍增预处理
f0/1,0/1,x,if_{0/1,0/1,x,i} 表示选/不选 xx ,选/不选 xx 向上走 2i2^i 步的祖先 yy 时,从 x>yx->y 的不包括 xx 这个点的子树的答案(即 yy 这个子树的答案扣除 xx 这个子树的答案)
转移很简单
f0,0,x,0=INFf_{0,0,x,0}=INF
f1,0,x,0=g0,fa0,xg1,xf_{1,0,x,0}=g_{0,fa_{0,x}}-g_{1,x}
f0,1,x,0=f1,1,x,0=g1,fa0,xmin(g0,x,g1,x)f_{0,1,x,0}=f_{1,1,x,0}=g_{1,fa_{0,x}}-min(g_{0,x},g_{1,x})
fj,k,x,i=min(fj,0,x,i1+f0,k,fai1,x,i1,fj,1,x,i1+f1,k,fai1,x,i1)f_{j,k,x,i}=min(f_{j,0,x,i-1}+f_{0,k,fa_{i-1,x},i-1},f_{j,1,x,i-1}+f_{1,k,fa_{i-1,x},i-1})
然后我们可以直接把 x,yx,ylcalca 的路径上的 ff 数组加起来,并加上 xx 的子树和 yy 的子树的 gg 值,再从 lcalca 开始加到根节点,就能算出总费用啦
问题来了,gg 值都是包含当前我们在做的链下面的子树的贡献的,比如我们要用新的 xxgg 值更新出新的 xx 的父亲 fafagg 值时,要计算 fafa 的别的子树的贡献,可是怎么处理出这一部分答案呢?
显然别的子树的答案是不会变的,而只有 xx 这个子树的 gg 值才会更改,所以我们可以先把这一部分在预处理时给 gfag_fa 的贡献值扣去,再加上新的值
所以我们可以得到
g0,fa+=newg1,xg_{0,fa}+=new-g_{1,x}
g1,fa+=newmin(g0,x,g1,x)g_{1,fa}+=new-min(g_{0,x},g_{1,x})
newnew 表示要新加的值
还有一些细节,具体看代码吧

#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;
}
posted @ 2018-11-27 13:14  xjqxjq  阅读(149)  评论(0编辑  收藏  举报