1. 程式人生 > >刷題總結——騎士的旅行(bzoj4336 樹鏈剖分套權值線段樹)

刷題總結——騎士的旅行(bzoj4336 樹鏈剖分套權值線段樹)

次數 || 會有 bzoj 表示 可能 clas calc() 輸入

題目:

Description

在一片古老的土地上,有一個繁榮的文明。 這片大地幾乎被森林覆蓋,有N座城坐落其中。巧合的是,這N座城由恰好N-1條雙 向道路連接起來,使得任意兩座城都是連通的。也就是說,這些城形成了樹的結構,任意兩 座城之間有且僅有一條簡單路徑。 在這個文明中,騎士是尤其受到尊崇的職業。任何一名騎士,都是其家族乃至家鄉的榮 耀。Henry從小就渴望成為一名能守護家鄉、驅逐敵人的騎士。勤奮訓練許多年後,Henry 終於滿18歲了。他決定離開家鄉,向那些成名已久的騎士們發起挑戰! 根據Henry的調查,大陸上一共有M名受封騎士,不妨編號為1到M。 第i個騎士居住在城Pi,武力值為Fi。 Henry計劃進行若幹次旅行,每次從某座城出發沿著唯一的簡單路徑前往另一座城, 同時會挑戰路線上武力值最高的K個騎士(Henry的體力有限,為了提高水平,當然要挑 戰最強的騎士)。如果路線上的騎士不足K人,Henry會挑戰遇到的所有人。 每次旅行前,可能會有某些騎士的武力值或定居地發生變化,Henry自然會打聽消息, 並對計劃做出調整。 為了在每次旅行時做好充分準備,Henry希望你能幫忙在每次旅行前計算出這條路線 上他將挑戰哪些對手。

Input

第一行,一個整數N,表示有N座城,編號為1~N。 接下來N-1行,每行兩個整數Ui和Vi,表示城Ui和城Vi之間有一條道路相連。 第N+1行,一個整數M,表示有M個騎士。 接下來M行,每行兩個整數Fi和Pi。按順序依次表示編號為1~M的每名騎士的武 力值和居住地。 第N+M+2行,兩個整數Q,K,分別表示操作次數和每次旅行挑戰的騎士數目上限。 接下來Q行,每行三個整數Ti,Xi,Yi。Ti取值範圍為{1,2,3},表示操作類型。 一共有以下三種類型的操作: Ti=1時表示一次旅行,Henry將從城Xi出發前往城市Yi; Ti=2時表示編號為Xi的騎士的居住地搬到城Yi; Ti=3時表示編號為Xi的騎士的武力值修正為Yi。

Output

輸出若幹行,依次為每個旅行的答案。 對每個Ti=1的詢問,輸出一行,按從大到小的順序輸出Henry在這次旅行中挑戰的 所有騎士的武力值。如果路線上沒有騎士,輸出一行,為一個整數-1。

Sample Input

5
1 2
1 3
2 4
2 5
4
10 1
6 1
14 5
7 3
5 3
1 2 3
1 5 3
1 4 4
2 1 4
1 2 3

Sample Output

10 7 6
14 10 7
-1
7 6

Hint

100%的數據中,1 ≤ N, M ≤ 40,000,1 ≤ Ui, Vi, Pi ≤ N,1 ≤ Q ≤ 80,000, 1 ≤ K ≤
20,旅行次數不超過 40,000 次,武力值為不超過1,000的正整數。

題解:

先樹鏈剖分····然後樹鏈剖分的每一個樹上套上一顆權值線段樹····

我的方法有點暴力···要輸入前K大直接一個一個找·····所以慢得飛起·····

代碼:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=4e5+5;
const int M=4e7+5;
struct node
{
  int size,l,r;
}tr[M];
int n,m,q,K;
int tot,fst[N],nxt[N*2],go[N*2],f[N],p[N],root[N*4],loc[N*4],cnt,sum[N*4],temp,que[N*4];
int father[N],deep[N],son[N],size[N],pos[N],idx[N],top[N];
inline int R()
{
  char c;int f=0;
  for(c=getchar();c<0||c>9;c=getchar());
  for(;c<=9&&c>=0;c=getchar())
    f=(f<<3)+(f<<1)+c-0;
  return f;
}
inline void comb(int a,int b)
{
  nxt[++tot]=fst[a],fst[a]=tot,go[tot]=b;  
  nxt[++tot]=fst[b],fst[b]=tot,go[tot]=a;
}
inline void dfs1(int u)
{
  size[u]=1;
  for(int e=fst[u];e;e=nxt[e])
  {
    int v=go[e];if(v==father[u])  continue;
    father[v]=u;deep[v]=deep[u]+1;
    dfs1(v);size[u]+=size[v];
    if(size[v]>size[son[u]])  son[u]=v;
  }
}
inline void dfs2(int u)
{
  if(son[u])
  {
    idx[pos[son[u]]=++tot]=son[u];
    top[son[u]]=top[u];dfs2(son[u]);
  }
  for(int e=fst[u];e;e=nxt[e])
  {
    int v=go[e];if(v==father[u]||v==son[u])  continue;
    idx[pos[v]=++tot]=v;
    top[v]=v;dfs2(v);
  }
}
inline void pre()
{
  dfs1(1);
  tot=pos[1]=idx[1]=top[1]=1;
  dfs2(1);
} 
inline void modify2(int &k,int l,int r,int v)
{
  if(!k)  k=++tot;tr[k].size++;
  if(l==r)  return;
  int mid=(l+r)/2;
  if(v<=mid)  modify2(tr[k].l,l,mid,v);
  else modify2(tr[k].r,mid+1,r,v);
}
inline void delete2(int k,int l,int r,int v)
{
  tr[k].size--;
  if(l==r)  return;
  int mid=(l+r)/2;
  if(v<=mid)  delete2(tr[k].l,l,mid,v);
  else delete2(tr[k].r,mid+1,r,v);
}
inline void modify1(int k,int l,int r,int p,int v)
{
  sum[k]++;
  modify2(root[k],1,1000,v);
  if(l==r)  return;
  int mid=(l+r)/2;
  if(p<=mid)  modify1(k*2,l,mid,p,v);
  else modify1(k*2+1,mid+1,r,p,v);
}
inline void delete1(int k,int l,int r,int p,int v)
{
  sum[k]--;
  delete2(root[k],1,1000,v);
  if(l==r)  return;
  int mid=(l+r)/2;
  if(p<=mid)  delete1(k*2,l,mid,p,v);
  else delete1(k*2+1,mid+1,r,p,v);
}
inline void getroot2(int k,int l,int r,int x,int y)
{
  if(x<=l&&r<=y)
  {
    que[++cnt]=root[k];temp+=sum[k];
    return;
  }
  int mid=(l+r)/2;
  if(x<=mid)  getroot2(k*2,l,mid,x,y);
  if(y>mid)  getroot2(k*2+1,mid+1,r,x,y);
}
inline void getroot1(int a,int b)
{
  if(top[a]!=top[b])
  {
    if(deep[top[a]]<deep[top[b]])  swap(a,b);
    getroot2(1,1,n,pos[top[a]],pos[a]);
    getroot1(father[top[a]],b);
  }
  else
  {
    if(deep[a]<deep[b])  swap(a,b);
    getroot2(1,1,n,pos[b],pos[a]);
  }
}
inline int calc()
{
  int t=0;
  for(int i=1;i<=cnt;i++)  t+=tr[tr[loc[i]].l].size;
  return t;
}
inline void trans(int op)
{
  if(!op)
    for(int i=1;i<=cnt;i++)  loc[i]=tr[loc[i]].l;
  else
    for(int i=1;i<=cnt;i++)  loc[i]=tr[loc[i]].r;
}
inline int query(int l,int r,int k)
{
  if(l==r)  return l;
  int t=calc();int mid=(l+r)/2;
  if(t>=k)
  {
    trans(0);
    return query(l,mid,k);
  }
  else 
  {
    trans(1);
    return query(mid+1,r,k-t);
  }
}
int main()
{
  n=R();int a,b,op;  
  for(int i=1;i<n;i++)
  {
    a=R(),b=R();
    comb(a,b);
  }
  pre(); 
  m=R();tot=0;
  for(int i=1;i<=m;i++)
  {  
    f[i]=R(),p[i]=R();
    modify1(1,1,n,pos[p[i]],f[i]);
  }
  q=R(),K=R();
  while(q--)
  {
    op=R(),a=R(),b=R();
    if(op==1)
    {
      temp=cnt=0;
      getroot1(a,b);
      if(!temp)  printf("-1");
      if(temp<=K)
      {
        for(int i=temp;i>=1;i--)
        {  
          for(int j=1;j<=cnt;j++)  loc[j]=que[j];
          printf("%d ",query(1,1000,i));
        }
      }
      else
      {
        for(int i=temp;i>=temp-K+1;i--)
        {  
          for(int j=1;j<=cnt;j++)  loc[j]=que[j];
          printf("%d ",query(1,1000,i));
        }
      }
      printf("\n");
    }
    else if(op==2)
    {
      delete1(1,1,n,pos[p[a]],f[a]);
      p[a]=b;
      modify1(1,1,n,pos[p[a]],f[a]);
    }
    else if(op==3)
    {
      delete1(1,1,n,pos[p[a]],f[a]);
      f[a]=b;
      modify1(1,1,n,pos[p[a]],f[a]);
    }
  }
}

刷題總結——騎士的旅行(bzoj4336 樹鏈剖分套權值線段樹)