【SP10628 COT - Count on a tree】题解
Description
静态区间第
不的不说,还真有点卡常,喜提最劣解。
温馨提示 : 这个题好像不能用 C ++ 11,14,17 来交,我交了后一直 waiting,建议用 C ++。
Solution
首先这个题目的数据范围也没说,我为了保险,还是写了个离散化,后来在原站上的知是在 int 范围内。
我用的是值域分块和树上莫队,查询区间第
树上莫队不会的请去 SP10707 学一下。
讲一下值域分块,我们分块的时候借用莫队的分块即可,树上莫队的区间长度是
所以只需要值域分块即可。
之后用两个数组
查询的时候,枚举每个块,之后查询即可。
int Work(int k)
{
int Sum = 0;//统计前面的数的个数
for(int i = 1;i <= T;i ++) {
if(Sum + tmp[i] < k) {Sum += tmp[i];continue;}
//如果这个块中的数字全部加起来都没有 k 大,就肯定不在这个块里面
else {
for(int j = L[i];j <= R[i];j ++) {
Sum += cnt[j];
//不断加上数值,直到达到目标。
if(Sum >= k) return j;
}
}
}
}
那么这是我们离散化后的数值,我们是要求原数值,我搞了个 map 来存,从
我建立了四个数组,
就这么卡过了。
献上我巨长的代码。(逃
Code
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
const int Maxk = 1e6 + 10;
int n,size,cnt1,m,Q,cnts,cntq,T;
int st[Maxk],ed[Maxk],sum[Maxk];
int tot[Maxk],dfn[Maxk << 1],top[Maxk];
int deep[Maxk],fa[Maxk],used[Maxk],cnt[Maxk];
int a[Maxk],son[Maxk],tmp[Maxk],b[Maxk],c[Maxk],d[Maxk];
int head[Maxk],L[Maxk],R[Maxk],block[Maxk];
int ans[Maxk],Ans;
struct node{
int from;
int to;
int next;
}e[Maxk << 1];
struct Query{
int l;
int r;
int pos;
int k_;
int Lca_;
}s[Maxk];
struct Modify {
int past;
int Now;
}q[Maxk];
inline int read()
{
int x = 0,f = 0;char ch = getchar();
while(!isdigit(ch)) f |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48),ch = getchar();
return f ? -x : x;
}
void add_edge(int from,int to)//连边
{
e[++ cnt1].to = to;
e[cnt1].from = from;
e[cnt1].next = head[from];
head[from] = cnt1;
return;
}
void BLOCK()
{
sort(b + 1,b + n + 1);
int tot = unique(b + 1,b + n + 1) - b - 1;
for(int i = 1;i <= n;i ++) {
a[i] = lower_bound(b + 1,b + tot + 1,a[i]) - b;//离散化
d[a[i]] = c[i];//建立一个关系
}
int nn = n << 1;//莫队的分块和值域分块
//这里我估计重新开一个长为 n 的块可能会更快,但我不想写了。
int len = pow(nn,0.6666667);
T = nn / len;
for(int i = 1;i <= T;i ++) {
L[i] = (i - 1) * len + 1;
R[i] = i * len;
}
if(R[T] < nn) T ++,L[T] = R[T - 1] + 1,R[T] = nn;
for(int i = 1;i <= T;i ++)
for(int j = L[i];j <= R[i];j ++)
block[j] = i;
return;
}
void dfs1(int now,int f)//树剖的 dfs1
{
deep[now] = deep[f] + 1;
fa[now] = f;
st[now] = ++ size;
dfn[size] = now;
tot[now] = 1;
for(int i = head[now];i;i = e[i].next) {
int y = e[i].to;
if(y == f) continue;
dfs1(y,now);
tot[now] += tot[y];
if(tot[y] > tot[son[now]]) son[now] = y;
}
ed[now] = ++size;
dfn[size] = now;
}
void dfs2(int now,int topfa)//树剖的 dfs2
{
top[now] = topfa;
if(!son[now]) return ;
dfs2(son[now],topfa);
for(int i = head[now];i;i = e[i].next) {
int y = e[i].to;
if(y == fa[now] || y == son[now]) continue;
dfs2(y,y);
}
}
int LCA(int x,int y)
//树上莫队判断是否需要加上 LCA
{
while(top[x] ^ top[y]) {
if(deep[top[x]] < deep[top[y]]) swap(x,y);
x = fa[top[x]];
}
return deep[x] < deep[y] ? x : y;
}
inline bool CMP(Query a,Query b)//莫队排序
{
return block[a.l] ^ block[b.l] ? block[a.l] < block[b.l] : a.r < b.r;
}
void Prepare()//处理询问,离线储存
{
for(int i = 1;i <= Q;i ++) {
int x = read(),y = read(),k = read();
if(st[x] > st[y]) swap(x,y);
int Lca = LCA(x,y);
s[i].k_ = k;
s[i].pos = i;
if(Lca == x) {//同一个子树内不用 LCA
s[i].l = st[x];
s[i].r = st[y];
}
else {
s[i].l = ed[x];//否则需要 LCA
s[i].r = st[y];
s[i].Lca_ = Lca;
}
}
return;
}
void add(int x)
{
cnt[x] ++;//这个值的个数 ++
tmp[block[x]] ++;//
}
void del(int x)
{
cnt[x] --;
tmp[block[x]] --;
}
void calc(int x)
{
used[x] ? del(a[x]) : add(a[x]);
used[x] ^= 1;
}
int Work(int k)//值域分块的查询第 k 小
{
int Sum = 0;
for(int i = 1;i <= T;i ++) {
if(Sum + tmp[i] < k) {Sum += tmp[i];continue;}
else {
for(int j = L[i];j <= R[i];j ++) {
Sum += cnt[j];
if(Sum >= k) return j;
}
}
}
}
void Solve()//莫队
{
sort(s + 1,s + Q + 1,CMP);
int l = 1,r = 0;
for(int i = 1;i <= Q;i ++) {
int ql = s[i].l;
int qr = s[i].r;
while(l < ql) calc(dfn[l ++]);
while(l > ql) calc(dfn[-- l]);
while(r > qr) calc(dfn[r --]);
while(r < qr) calc(dfn[++ r]);
if(s[i].Lca_) calc(s[i].Lca_);
ans[s[i].pos] = d[Work(s[i].k_)];
if(s[i].Lca_) calc(s[i].Lca_);
}
for(int i = 1;i <= Q;i ++) printf("%d\n",ans[i]);
return;
}
signed main()
{
n = read(),Q = read();
for(int i = 1;i <= n;i ++) a[i] = b[i] = c[i] = read();//读入
for(int i = 1;i <= n - 1;i ++) {//连边
int x = read(),y = read();
add_edge(x,y);
add_edge(y,x);
}
BLOCK();//分块 + 离散化
dfs1(1,0);//树剖
dfs2(1,1);
Prepare();//读入询问
Solve();//莫队解决
return 0;
}