[HAOI2011]Problem b 题解
题目地址:洛谷:【P2522】[HAOI2011]Problem b – 洛谷 …
May all the beauty be blessed.
题目地址:洛谷:【P2664】树上游戏 – 洛谷
lrb有一棵树,树的每个节点有个颜色。给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量。以及
sum_i = \sum_{j=1}^n s(i, j)
现在他想让你求出所有的sum[i]
输入格式:
第一行为一个整数n,表示树节点的数量
第二行为n个整数,分别表示n个节点的颜色c[1],c[2]……c[n]
接下来n-1行,每行为两个整数x,y,表示x和y之间有一条边
输出格式:
输出n行,第i行为sum[i]
输入样例#1:
5 1 2 3 2 3 1 2 2 3 2 4 1 5
输出样例#1:
10 9 11 9 12
sum[1]=s(1,1)+s(1,2)+s(1,3)+s(1,4)+s(1,5)=1+2+3+2+2=10
sum[2]=s(2,1)+s(2,2)+s(2,3)+s(2,4)+s(2,5)=2+1+2+1+3=9
sum[3]=s(3,1)+s(3,2)+s(3,3)+s(3,4)+s(3,5)=3+2+1+2+3=11
sum[4]=s(4,1)+s(4,2)+s(4,3)+s(4,4)+s(4,5)=2+1+2+1+3=9
sum[5]=s(5,1)+s(5,2)+s(5,3)+s(5,4)+s(5,5)=2+3+3+3+1=12
对于40%的数据,n<=2000
对于100%的数据,1<=n,c[i]<=10^5
参考资料:题解 P2664 【树上游戏】 – Salamander 的博客 – 洛谷博客
超麻烦的点分治,说实话我在写这篇博文的时候自己都不是很懂。
对于每一个重心,我们先计算其子树内点对它答案的贡献,该贡献即为每种颜色在每条路径第一次出现的位置对应的子树大小之和。我们把子树中每种颜色在每条路径第一次出现的位置的子树大小记为cnt数组,该数组的意义实际上就是包含该种颜色的路径数,则子树内点对重心的贡献就是对cnt数组求和。
接着考虑计算经过重心的路径的贡献。对于每个单独的子树,我们可以求出类似上述cnt数组定义的ct数组。对于一个出现在重心到子树节点x的路上上的颜色col,它会与除了该子树以外的其他到重心路径上无该颜色的点组成一条没有被计算过的路径,因此该颜色会对该点的sum数组产生(siz[rt]-siz[v])-(cnt[col]-ct[col])这么多贡献,同时该颜色也会对该点的子树产生贡献,因此要把这个贡献给传递到子树中去。另外,还有可能在别的子树出现过的颜色对该点的贡献,实际上就是对cnt[col]-ct[col]求和。
总复杂度O(n \log n)。
// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <vector>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(!isdigit(c)) {
if(c == '-') neg = -1;
c = fgc();
}
while(isdigit(c)) {
res = (res << 1) + (res << 3) + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 100005;
std::vector<int> gra[MAXN];
int n, c[MAXN];
int siz[MAXN], rt, rtsiz;
bool vis[MAXN];
inline void findrt(int u, int fa, int tot) {
siz[u] = 1; int mxsiz = 0;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(vis[v] || v == fa) continue;
findrt(v, u, tot);
siz[u] += siz[v];
mxsiz = std::max(mxsiz, siz[v]);
}
mxsiz = std::max(mxsiz, tot - siz[u]);
if(mxsiz < rtsiz) {
rt = u; rtsiz = mxsiz;
}
}
inline void calsiz(int u, int fa) {
siz[u] = 1;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(vis[v] || v == fa) continue;
calsiz(v, u);
siz[u] += siz[v];
}
}
int cnt[MAXN], cct[MAXN], ct[MAXN], col[MAXN], cl[MAXN], top, tp, tot, path;
LL sum[MAXN];
int has[MAXN];
bool exist[MAXN];
inline void dfs(int u, int fa, int *cnt) {
if(!exist[c[u]]) {
col[++top] = c[u]; exist[c[u]] = true;
}
if(++has[c[u]] == 1) cnt[c[u]] += siz[u];
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(vis[v] || v == fa) continue;
dfs(v, u, cnt);
}
has[c[u]]--;
}
inline void modify(int u, int fa, LL lst) {
LL tag = lst;
if(++has[c[u]] == 1) tag += path - cnt[c[u]];
sum[u] += tot + tag;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(vis[v] || v == fa) continue;
modify(v, u, tag);
}
has[c[u]]--;
}
inline void calc(int u) {
calsiz(u, 0);
top = tot = 0;
dfs(u, 0, cnt);
for(int i = 1; i <= top; i++) {
exist[col[i]] = false;
}
tp = top;
for(int i = 1; i <= top; i++) {
tot += cnt[cl[i] = col[i]];
cct[col[i]] = cnt[col[i]];
}
sum[u] += tot;
int temp = tot;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(vis[v]) continue;
has[c[u]] = true; top = 0;
dfs(v, u, ct);
has[c[u]] = false;
for(int j = 1; j <= top; j++) {
exist[col[j]] = false;
}
cnt[c[u]] -= siz[v];
tot -= siz[v];
for(int j = 1; j <= top; j++) {
cnt[col[j]] -= ct[col[j]];
tot -= ct[col[j]];
}
path = siz[u] - siz[v];
modify(v, u, 0);
cnt[c[u]] += siz[v];
tot = temp;
for(int j = 1; j <= top; j++) {
cnt[col[j]] = cct[col[j]];
ct[col[j]] = 0;
}
}
for(int i = 1; i <= tp; i++) {
cnt[cl[i]] = 0;
}
vis[u] = true;
}
inline void divide(int u) {
calc(u);
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(vis[v]) continue;
rt = 0; rtsiz = n; findrt(v, u, siz[v]);
divide(rt);
}
}
int main() {
n = readint();
for(int i = 1; i <= n; i++) {
c[i] = readint();
}
for(int i = 1, u, v; i < n; i++) {
u = readint(); v = readint();
gra[u].push_back(v); gra[v].push_back(u);
}
rt = 0; rtsiz = n; findrt(1, 0, n);
divide(rt);
for(int i = 1; i <= n; i++) {
printf("%lld\n", sum[i]);
}
return 0;
}
题目地址:洛谷:【P2571】[SCOI2010]传送带 – 洛谷、BZOJ:Problem 1857. — [Scoi2010]传送带
在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段。两条传送带分别为线段AB和线段CD。lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R。现在lxhgww想从A点走到D点,他想知道最少需要走多长时间
输入格式:
输入数据第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By
第二行是4个整数,表示C和D的坐标,分别为Cx,Cy,Dx,Dy
第三行是3个整数,分别是P,Q,R
输出格式:
输出数据为一行,表示lxhgww从A点走到D点的最短时间,保留到小数点后2位
输入样例#1:
0 0 0 100 100 0 100 100 2 2 1
输出样例#1:
136.60
对于100%的数据,1<= Ax,Ay,Bx,By,Cx,Cy,Dx,Dy<=1000,1<=P,Q,R<=10
我们在AB上取一个点E,CD上取一个点F,路径一定是A→E→F→D这样的。我们来大胆猜测一波,如果固定E,时间关于F在CD上的位置是一个单峰有最小值的函数,再猜测总时间关于E在AB上的位置也是这样一个函数,那么我们就可以三分套三分来解决这个问题。可是我不会证明啊QAQ
三分法是用于解决单峰函数找最值的问题的方法,具体操作可以参见【P3382】【模板】三分法 – 洛谷。
// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>
#include <cmath>
const double EPS = 1e-8;
double ax, ay, bx, by, cx, cy, dx, dy, p, q, r;
// E on AB, F on CD
inline double caltime(double ex, double ey, double fx, double fy) {
return sqrt((ex - ax) * (ex - ax) + (ey - ay) *(ey - ay)) / p
+ sqrt((dx - fx) * (dx - fx) + (dy - fy) *(dy - fy)) / q
+ sqrt((ex - fx) * (ex - fx) + (ey - fy) *(ey - fy)) / r;
}
inline double terCD(double ex, double ey) {
double l = 0, r = 1, mid, midd;
while(r - l > EPS) {
mid = (l + r) / 2; midd = (mid + r) / 2;
if(caltime(ex, ey, cx + (dx - cx) * mid, cy + (dy - cy) * mid) <
caltime(ex, ey, cx + (dx - cx) * midd, cy + (dy - cy) * midd)) {
r = midd;
} else {
l = mid;
}
}
return caltime(ex, ey, cx + (dx - cx) * l, cy + (dy - cy) * l);
}
inline double terAB() {
double l = 0, r = 1, mid, midd;
while(r - l > EPS) {
mid = (l + r) / 2; midd = (mid + r) / 2;
if(terCD(ax + (bx - ax) * mid, ay + (by - ay) * mid) <
terCD(ax + (bx - ax) * midd, ay + (by - ay) * midd)) {
r = midd;
} else {
l = mid;
}
}
return terCD(ax + (bx - ax) * l, ay + (by - ay) * l);
}
int main() {
scanf("%lf%lf%lf%lf", &ax, &ay, &bx, &by);
scanf("%lf%lf%lf%lf", &cx, &cy, &dx, &dy);
scanf("%lf%lf%lf", &p, &q, &r);
printf("%.2lf", terAB());
return 0;
}
题目地址:洛谷:【P3332】[ZJOI2013]K大数查询 – 洛谷、BZOJ:Problem 3110. — [Zjoi2013]K大数查询
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
输入格式:
第一行N,M接下来M行,每行形如1 a b c或2 a b c
输出格式:
输出每个询问的结果
输入样例#1:
2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3
输出样例#1:
1 2 1
【样例说明】
第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3大的数是 1 。
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=long long
可以用线段树套线段树的写法。我们在外面维护一个权值线段树,每个节点挂一个普通的区间求和线段树,这样,我们就可以在利用权值线段树查询k大的时候,通过普通线段树查到询问区间内的数字个数。总复杂度O(n \log^2 n)可以接受,但是这种方法常数较大,洛谷不开O2无法通过,且需要动态开点,否则容易爆内存。
// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(!isdigit(c)) {
if(c == '-') neg = -1;
c = fgc();
}
while(isdigit(c)) {
res = (res << 1) + (res << 3) + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 100005;
struct SGTNode {
int lch, rch;
LL tag, val;
} sgt[MAXN * 200];
int stot;
inline void pushdown(int o, int l, int r) {
int mid = (l + r) >> 1;
if(sgt[o].tag) {
if(!sgt[o].lch) sgt[o].lch = ++stot;
sgt[sgt[o].lch].val += sgt[o].tag * (mid - l + 1);
sgt[sgt[o].lch].tag += sgt[o].tag;
if(!sgt[o].rch) sgt[o].rch = ++stot;
sgt[sgt[o].rch].val += sgt[o].tag * (r - mid);
sgt[sgt[o].rch].tag += sgt[o].tag;
sgt[o].tag = 0;
}
}
struct SegmentTree {
int rt;
inline void add(int &o, int l, int r, int ll, int rr, int v) {
if(!o) o = ++stot;
if(l >= ll && r <= rr) {
sgt[o].tag += v;
sgt[o].val += v * (r - l + 1);
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1;
if(ll <= mid) add(sgt[o].lch, l, mid, ll, rr, v);
if(rr > mid) add(sgt[o].rch, mid + 1, r, ll, rr, v);
sgt[o].val = sgt[sgt[o].lch].val + sgt[sgt[o].rch].val;
}
inline LL query(int o, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) {
return sgt[o].val;
}
pushdown(o, l, r);
int mid = (l + r) >> 1; LL res = 0;
if(ll <= mid) res += query(sgt[o].lch, l, mid, ll, rr);
if(rr > mid) res += query(sgt[o].rch, mid + 1, r, ll, rr);
return res;
}
} sts[MAXN << 2];
int n, m;
#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)
inline void insert(int o, int l, int r, int x, int il, int ir) {
sts[o].add(sts[o].rt, 1, n, il, ir, 1);
if(l == r) return;
if(x <= mid) insert(lch, l, mid, x, il, ir);
else insert(rch, mid + 1, r, x, il, ir);
}
inline int query(int o, int l, int r, LL k, int ql, int qr) {
if(l == r) return l;
LL rsiz = sts[rch].query(sts[rch].rt, 1, n, ql, qr);
if(k <= rsiz) return query(rch, mid + 1, r, k, ql, qr);
else return query(lch, l, mid, k - rsiz, ql, qr);
}
int op, a, b; LL c;
int main() {
n = readint(); m = readint();
while(m--) {
op = readint(); a = readint(); b = readint(); c = readint();
if(op == 1) insert(1, 0, n << 1, c + n, a, b);
if(op == 2) printf("%d\n", query(1, 0, n << 1, c, a, b) - n);
}
return 0;
}