C. Constructing Ranches
題意:
一棵樹上每個節點有權值\(a_i\),問你有多少路徑滿足以下條件:路徑經過的每個點權作為邊長,可以構成一個多邊形。
解法:
三條邊能構成三角形的條件是兩邊之和大於第三邊,四邊形是三邊之和大於第四邊,以此類推。轉化一下條件就是\(sum>2*max\)
考慮點分治。對於每個選定的重心,我們假設\(sum_i\)表示i到重心的路徑上的點權和,\(mx_i\)表示i到重心路徑上最大點權。
那么經過重心x符合條件的一條路徑就有\(sum_u+sum_v-a_x>2*max(mx_u,mx_v)\)
那么我們對當前處理的所有結點按照\(mx_i\)排序一下,另外一個結點的sum就是定值了,用樹狀數組可以維護個數。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5;
ll ans;
int root,minn,all;
bool vis[maxn + 11] = {false};
vector <pair<ll,ll> > v;
vector <ll> s;
int a[maxn + 11],siz[maxn + 11],f[maxn + 11];
int bit[maxn + 11];
vector <int> edge[maxn + 11];
int lowbit(int x) { return x & (-x); }
int query(int x) { int ans = 0; for (; x; x -= lowbit(x)) ans += bit[x]; return ans; }
void update(int x,int val,int n) { for (; x <= n; x += lowbit(x)) bit[x] += val; }
void dfs(int x,int fa) {
siz[x] = 1; f[x] = 0;
for (auto v : edge[x]) {
if (v == fa || vis[v]) continue;
dfs(v , x);
siz[x] += siz[v];
f[x] = max(f[x] , siz[v]);
}
f[x] = max(f[x] , all - siz[x]);
if (f[x] < minn) { minn = f[x]; root = x; }
}
void find(int x,int fa,ll sum,int mx) {
v.push_back({max(mx , a[x]) , sum + a[x]});
for (auto v : edge[x]) {
if (v == fa || vis[v]) continue;
find(v , x , sum + a[x] , max(mx , a[x]));
}
}
ll calc(int x,ll val) {
find(x , 0 , val , val);
sort(v.begin() , v.end());
ll cnt = 0;
ll add = val ? val : a[x];
for (auto pi : v) s.push_back(pi.second);
sort(s.begin() , s.end());
s.erase(unique(s.begin() , s.end()) , s.end());
for (int i = 0; i < v.size(); i++) {
pair <ll,ll> pi = v[i];
int pos = upper_bound(s.begin() , s.end() , 2 * pi.first + add - pi.second) - s.begin();
cnt += i - query(pos);
pos = lower_bound(s.begin() , s.end() , pi.second) - s.begin() + 1;
update(pos , 1 , s.size());
}
for (auto pi : v) {
int pos = lower_bound(s.begin() , s.end() , pi.second) - s.begin() + 1;
update(pos , -1 , s.size());
}
return cnt;
}
void solve(int x) {
vis[x] = true;
ans += calc(x , 0); v.clear(); s.clear();
for (auto V : edge[x]) {
if (vis[V]) continue;
ans -= calc(V , a[x]); v.clear(); s.clear();
}
for (auto v : edge[x]) {
if (vis[v]) continue;
minn = all = siz[v]; root = 0;
dfs(v , x);
solve(root);
}
}
int main(){
int t;
scanf("%d" , &t);
while (t--) {
int n;
scanf("%d" , &n);
for (int i = 1; i <= n; i++) scanf("%d" , &a[i]);
for (int i = 1; i < n; i++) {
int u,v;
scanf("%d %d" , &u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
root = 0; minn = all = n; ans = 0;
dfs(1 , 0);
solve(root);
printf("%lld\n" , ans);
for (int i = 1; i <= n; i++) siz[i] = f[i] = 0,vis[i] = false,edge[i].clear();
}
}
H. Hold the Line
題意:
一個區間[1,n],兩種操作。第一種操作將位置i的值設為x,第二種操作求[l,r]這個區間內與h最相近的權值。操作具有先后順序,每個位置只能賦值一次。
解法
考慮離線做法。每次枚舉右端點r=1,2,3...來依次修改,查詢操作。線段樹維護[a,b]表示h在[a,b]之間的<pos,time>。
因為位置是從左向右依次插入的,我們考慮\(pos_j\leq pos_k\),如果\(time_j>time_k\),那么j這個值是沒有意義的。因為在同一高度區間內,我們詢問時需要的是在某個時間之前插入的且位置處於[l,r]之間的,如果一個值,位置更靠左且時間更靠后,那么這個點顯然是無用的。所以我們對於線段樹上的每個結點用一個單調隊列進行維護即可。
#include <bits/stdc++.h>
#define all(x) (x).begin(),(x).end()
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
const int inf = 2e9;
const int maxn = 1e6;
struct Query{
int l,h,id;
};
vector <pair <int,int> > que[4 * maxn + 11];
int ans[maxn + 11];
pair <int,int> ins[maxn + 11] = make_pair(0 , 0);
vector <int> v;
vector <Query> query[maxn + 11];
void update(int rt,int l,int r,int h,int pos,int time) {
if (l > h || r < h) return;
while (!que[rt].empty() && que[rt].back().second > time) que[rt].pop_back(); que[rt].push_back({pos , time});
if (l == r) return;
int mid = (l + r) >> 1;
update(lson , l , mid , h , pos , time);
update(rson , mid + 1 , r , h , pos , time);
}
int find_min(int rt,int l,int r,int pos,int time) {
if (l == r) return l;
int mid = (l + r) >> 1;
if (que[rson].empty() || que[rson].back().first < pos) return find_min(lson , l , mid , pos , time);
int ind = lower_bound(all(que[rson]) , make_pair(pos , -1)) - que[rson].begin();
if (que[rson][ind].second < time) return find_min(rson , mid + 1 , r , pos , time);
return find_min(lson , l , mid , pos , time);
}
int query_min(int rt,int l,int r,int al,int ar,int pos,int time) {
if (l > ar || r < al || que[rt].empty()) return 0;
if (l >= al && r <= ar) {
if (que[rt].back().first < pos) return 0;
int ind = lower_bound(all(que[rt]) , make_pair(pos , -1)) - que[rt].begin();
if (que[rt][ind].second > time) return 0;
return find_min(rt , l , r , pos , time);
}
int mid = (l + r) >> 1;
int ret = query_min(rson , mid + 1 , r , al , ar , pos , time);
if (!ret) return query_min(lson , l , mid , al , ar , pos , time);
return ret;
}
int find_max(int rt,int l,int r,int pos,int time) {
if (l == r) return l;
int mid = (l + r) >> 1;
if (que[lson].empty() || que[lson].back().first < pos) return find_max(rson , mid + 1 , r , pos , time);
int ind = lower_bound(all(que[lson]) , make_pair(pos , -1)) - que[lson].begin();
if (que[lson][ind].second < time) return find_max(lson , l , mid , pos , time);
return find_max(rson , mid + 1 , r , pos , time);
}
int query_max(int rt,int l,int r,int al,int ar,int pos,int time) {
if (l > ar || r < al || que[rt].empty()) return inf;
if (l >= al && r <= ar) {
if (que[rt].back().first < pos) return inf;
int ind = lower_bound(all(que[rt]) , make_pair(pos , -1)) - que[rt].begin();
if (que[rt][ind].second > time) return inf;
return find_max(rt , l , r , pos , time);
}
int mid = (l + r) >> 1;
int ret = query_max(lson , l , mid , al , ar , pos , time);
if (ret == inf) return query_max(rson , mid + 1 , r , al , ar , pos , time);
return ret;
}
int main(){
int t;
scanf("%d" , &t);
while (t--) {
int n,m;
scanf("%d %d",&n,&m);
int o = m;
for (int i = 1; i <= m; i++) {
ans[i] = -1;
int op;
scanf("%d" , &op);
if (op == 0) {
int x,h;
scanf("%d %d",&x,&h);
ins[x] = {h , i};
v.push_back(h);
}
else {
int l,r,h;
scanf("%d %d %d",&l,&r,&h);
query[r].push_back({l , h , i});
v.push_back(h);
}
}
sort(all(v)); v.erase(unique(all(v)) , v.end());
m = v.size();
for (int i = 1; i <= n; i++) {
if (ins[i].first) {
int H = lower_bound(all(v) , ins[i].first) - v.begin() + 1;
update(1 , 1 , m , H , i , ins[i].second);
}
for (auto pi : query[i]) {
ans[pi.id] = inf;
int H = lower_bound(all(v) , pi.h) - v.begin() + 1;
if (H > 1) {
int l = query_min(1 , 1 , m , 1 , H - 1 , pi.l , pi.id);
if (l != 0) ans[pi.id] = min(ans[pi.id] , pi.h - v[l - 1]);
}
int r = query_max(1 , 1 , m , H , m , pi.l , pi.id);
if (r != inf) ans[pi.id] = min(ans[pi.id] , v[r - 1] - pi.h);
}
}
for (int i = 1; i <= o; i++) {
if (ans[i] == -1) continue;
if (ans[i] == inf) printf("-1\n");
else printf("%d\n" , ans[i]);
}
v.clear();
for (int i = 1; i <= n; i++) query[i].clear(),ins[i] = make_pair(0 , 0);
for (int i = 1; i <= 4 * m; i++) que[i].clear();
}
}