傳送門
想題5分鐘,調題20小時\(qaq\)
我做這題的歷程:
這什麼破玩意兒
如果您聽說過「替罪羊式重構點分樹」的話這題就很好想了。
首先考慮樸素的點分治:
每次分治統計路徑經過分治中心的點對的貢獻。
記\(dis_i\)為點\(i\)到分治中心的距離,條件即\(dis_i+dis_j\le r_i+r_j\)。
移個項:\(dis_i-r_i\le r_j-dis_j\)。
把\(r_i-dis_i\)插進平衡樹里,查詢平衡樹中大於等於\(dis_i-r_i\)的數量。
動態的做法就很明顯了,點分樹上每個點開個平衡樹。還要容斥減掉同一子樹的貢獻,用\(vector\)記錄每層上用於容斥的平衡樹。
加點直接在點分樹上將\(i\)與\(a_i\)連邊,暴力跳點分樹在平衡樹里查,並把自己的資訊插進去。
注意到每次都會在某個點下方掛乙個點,而點分樹的樹高要維持在\(\log\)級別。
很像平衡樹。旋轉是不可能了,利用替罪羊樹的思想重構點分樹。
記\(siz\)為點分樹上子樹大小。當\(\max\limits_siz_i\ge siz_x\times \alpha\)(\(\alpha\)為平衡因子,我定的\(0.7\))就重構點\(i\)的子樹。
\(asuldb\):重構多簡單,不就點分治一下嗎。重構我們要幹啥(\(x\)為待重構點):
為了保證單次重構複雜度是\(o(siz_x\log siz_x)\)的,把子樹資訊提出來排個序直接build
。\(treap\)有隨機權值不好build
,\(splay\)太慢於是選擇替罪羊樹。
(其實假了,還有個排序重構是\(o(siz_x\log^2 siz_x)\)的。。。)
還要帶加點查詢點對的距離,可以倍增,我用的\(lct\)。
複雜度為\(o(n\log^2 n)\)。
注意垃圾**。
全是細節的**:
#include #include #include #include #include #include #include #define maxn 100005
#define inf 0x3f3f3f3f
const double alpha = 0.7;
const int mod = 1e9;
using namespace std;
inline int read()
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}struct edgee[maxn<<1];
struct scapegoattree
void build(int *base,int l,int r,int &node)
node=pool[cnt--];
int mid=l+r>>1;
dat[node]=base[mid];
build(base,l,mid-1,ls(node));
build(base,mid+1,r,rs(node));
siz[node]=siz[ls(node)]+siz[rs(node)]+1;
}void dfs(int node)
int push(int &node,int d)
++siz[node];
int p=push(son(node,dat[node]=x)ans+=siz[rs(node)]+1,node=ls(node);
else node=rs(node);
}return ans;
}scapegoattree()
}sgt;
struct linkcuttree
inline void addedge(int s,int f,int wh)
inline void reverdown(int node)
inline void pushdown(int node)
void zhuan(int x)
void splay(int x)
}void access(int x)
void makeroot(int x)
void link(int x,int y)
int get(int x,int y)
}lct;
int r1[maxn],r2[maxn],h[maxn],siz[maxn],ts[maxn],pool[maxn<<2],num,cnt=(maxn<<2)-1,all,mx,root,l1,l2;
int mp[maxn],rt[maxn],exc[maxn<<3],line[maxn],dep[maxn],fa[maxn],a[maxn];
bool vis[maxn],rub[maxn<<1];
vectorb[maxn];
setson[maxn];
inline void add(int from,int to,int l)
void getroot(int node,int f=0)
ma=max(ma,all-siz[node]);
if(ma::iterator iter=son[x].begin();iter!=son[x].end();++iter)
dep[line[++tail]=*iter]=dep[x]+1;
while(dep[x]--)
sgt.clear(rt[x]);
son[x].clear();
}for(register int i=rc+1;i<=cnt;++i)rub[pool[i]]=0;
}void dfs(int node,int len,int f)
}void build(int node)
sort(r1+1,r1+1+l1);
sgt.build(r1,1,l1,rt[node]);
for(register int i=h[node];i;i=e[i].pre)
}void rebuild(int node)
if(p)
}int main()
printf("%lld\n",ans);
rebuild(i);}}
洛谷P2014 選課
題目描述 在大學裡每個學生,為了達到一定的學分,必須從很多課程裡選擇一些課程來學習,在課程裡有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有n門功課,每門課有個學分,每門課有一門或沒有直接先修課 若課程a是課程b的先修課即只有學完了課程a,才能學習課程b 乙個學生要從這些課程...
洛谷P2014 選課
又是一道樹型dp,不過這次是以點帶權值,因為根是不確定的,我們可以設個虛根 0 因為算是多了一點,所以總點數應該 因為是點帶權值,所以不用dfs邊的數量了,不過有一點虛注意,因為多了乙個點,所以j層迴圈 所選的邊數 下界應該是到2的。include include using namespace s...
洛谷P2014 選課
題目描述 在大學裡每個學生,為了達到一定的學分,必須從很多課程裡選擇一些課程來學習,在課程裡有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有n門功課,每門課有個學分,每門課有一門或沒有直接先修課 若課程a是課程b的先修課即只有學完了課程a,才能學習課程b 乙個學生要從這些課程...