目錄每個節點維護一課線段樹(當然是動態開點)
線段樹的作用是統計這個節點有多少種糧食型號,以及最多的糧食型號
然後樹上差分,u和v點 +1,lca(u,v)和f[lca(u,v)] -1(不顯然就畫圖嘍)
並不用轉化為dfs序
只需要dfs一邊,自底向上合併就好
刪除節點不必建樹
因為在遞迴到他的時候一定是存在的
(要不 他還能減成負數嗎、、)
但在一開始建樹的時候就不一定存在了
所以記錄下來就好
這合併真的靠譜嗎
如果兩個線段樹都是滿二叉那不涼涼了
先引用部落格大佬一段:
這樣子做時間複雜度取決於重合節點個數
因為滿二叉樹的結點數是o(n)
對每個結點進行處理是o(logn)
最壞複雜度是o(nlogn),
看著很涼,但是一點也不涼(說出了什麼b話)
假設乙個為滿二叉
最壞複雜度的情況一定是重合部分最多的時候
也就是每條鏈都是分開的(一條鍊子合併複雜度最壞為logn)
一次修改4個點,一共修改4n個點 (n,m同階的話)
那複雜度就是4nlogn也就是nlogn了
平均每棵樹4點,實際上也不會每次修改logn
emma,應該是這樣
n並不是救濟糧型號的範圍
所以建樹應該是1到10w而不是n
zz了,2333
//¢¢£
#include #define for(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int maxn=1e5+7;
const int n=1e5;
inline int read()
int n,m,ans[maxn],cnt,rt[maxn];
vectordelet[maxn];
int ch[maxn*50][2],ma[maxn*50],id[maxn*50];
//關於圖
struct edge e[maxn<<1];
int head[maxn<<1],tot;
void add_edge(int u,int v)
namespace lca
} int lca(int x,int y)
void init()
}void pushup(int now)
void build(int &now,int l,int r,int k,int up)
int mid=(l+r)>>1;
if(k<=mid) build(ch[now][0],l,mid,k,up);
else build(ch[now][1],mid+1,r,k,up);
pushup(now);
}int merge(int l,int r,int x,int y)
int mid=(l+r)>>1;
ch[x][0]=merge(l,mid,ch[x][0],ch[y][0]);
ch[x][1]=merge(mid+1,r,ch[x][1],ch[y][1]);
pushup(x);
return x;
}void dfs(int u,int f)
for(vector::iterator it=delet[u].begin();it!=delet[u].end();++it)
build(rt[u],1,n,*it,-1);
ans[u]=id[rt[u]];
}int main()
lca::init();
for(i,1,m)
dfs(1,0);
for(i,1,n) cout
}
Vani有約會 雨天的尾巴
我之前考試是遇到過這題,但是資料範圍k 20,狀壓就能過。結果原題範圍k 100000 果斷線段樹合併。比如兩個相同大小的線段樹,將b樹各個區間上的值合併到a樹上,從樹根開始合併,然後遞迴合併左右兒子,有三種情況 假設現在a樹遍歷到x點,b樹遍歷到y點 1.x,y至少其一未被修改過 語文不好勿噴 則...
Vani有約會 雨天的尾巴
題意 有一棵n個點的樹,m次操作,每次操作給路徑 u,v 上每個點發乙個型別為w的物品。在所有操作後請你求出每個點個數最多的物品型別。n,m,w leq 10 題解 樹鏈剖分基本就是把序列問題上樹,於是考慮序列怎麼做,直接把區間加改成差分再維護一棵權值線段樹即可。複雜度 o n log 注意那個w不...
Vani有約會 雨天的尾巴 線段樹合併
樹上差分 線段樹合併.在每個節點上維護一棵權值線段樹.然後如果需要修改 x,y 兩點,則在 x 處和 y 處分別加上 1 的權值.然後在 lca x,y 以及 fa lca x,y 處減掉 1 最後面 dfs 從下往上更新.由於每一次維護只維護四個點的值,且每次在每一棵樹上也只會修改一條鏈的值.每次...