整体二分
- 整体二分一般适用与可离线询问的问题,属于离线算法
-
可以使用整体二分解决的题目需要满足以下性质:
-
询问的答案具有可二分性
-
修改对判定答案的贡献互相独立 ,修改之间互不影响效果
-
修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值
-
贡献满足交换律,结合律,具有可加性
-
题目允许使用离线算法
- 题目链接: https://www.luogu.com.cn/problem/P3834
- 解题思路: 无
const int N = 2e5 + 5;
int num[N];
int n , m;
struct node1
{
int x, val;
};
struct node2 {
int id , l , r , k;
};
int ans[N];
vector <node1> a;
vector <node2> q;
struct BIT
{
int tree[N] ;
void init()
{
mem(tree,0) ;
}
int lowbit(int k)
{
return k & -k;
}
void add(int x , int k)
{
while(x <= n)
{
tree[x] += k ;
x += lowbit(x) ;
}
}
int sum(int x)
{
int ans = 0 ;
while(x != 0)
{
ans += tree[x] ;
x -= lowbit(x) ;
}
return ans ;
}
int query(int l , int r)
{
return sum(r) - sum(l - 1) ;
}
} bit ;
void solve (ll L, ll R, vector <node1> a, vector<node2> q)
{
if (!a.size() || !q.size())
return ;
bit.init();
ll mid = (L + R) >> 1;
if (L == R)
{
for (auto v : q)
ans[v.id] = L;
return ;
}
vector<node1> a1, a2;
vector<node2> q1, q2;
for (auto v : a)
{
if (v.val <= mid)
a1.pb(v), bit.add (v.x , 1);
else
a2.pb(v);
}
for (auto v : q)
{
int l = v.l , r = v.r, k = v.k;
int t = bit.query(l, r);
if (k <= t)
q1.pb(v);
else
q2.pb({v.id,v.l,v.r,v.k - t});
}
solve (L, mid, a1, q1);
solve (mid + 1 , R , a2, q2);
}
int main()
{
read (n, m);
for (int i = 1 ; i <= n ; i ++) {
read(num[i]);
a.pb ({i, num[i]});
}
for (int i = 1 ; i <= m ; i ++)
{
int l , r, k;
read (l ,r , k);
q.pb({i , l , r , k});
}
solve (-INF, INF, a, q);
for (int i = 1 ; i <= n ; i ++)
write (ans[i]), LF;
}
- 题目链接: https://codeforces.com/contest/1039/problem/D
- 解题思路: 本题先考虑 O ( n 2 ) O(n^2) O(n2)如何去做,我们可以枚举长度 x x x然后自底向上贪心的去处理。对于每一个 x x x对应答案这个复杂度我们不好优化,于是我们考虑是否可以优化 x x x,我们发现 x x x对应的答案存在单调性,于是我们使用整体二分优化枚举
const int N = 2e5 + 5;
int n, ans[N], cnt;
vector <int> edge[N];
int dp[N];
void dfs (int u ,int fa, int k)
{
dp[u] = 0;
int mx1 = 0 , mx2 = 0;
for (auto v : edge[u])
{
if (v != fa)
{
dfs (v, u, k);
if (dp[v] > mx1) mx2 = mx1, mx1 = dp[v];
else mx2 = max(mx2, dp[v]);
}
}
if (mx1 + mx2 + 1 >= k) dp[u] = 0, cnt ++;
else dp[u] = mx1 + 1;
}
void solve (int l , int r, int L , int R)
{
if (l > r || L > R) return ;
if (L == R)
{
for (int i = l ; i <= r ; i ++)
ans[i] = L;
return ;
}
cnt = 0;
int mid = (l + r) >> 1;
dfs (1, 0 , mid);
ans[mid] = cnt;
solve (l, mid-1, cnt, R);
solve (mid+1, r, L, cnt);
}
int main ()
{
CLOSE;
cin >> n;
for (int i = 1 ; i < n ; i ++)
{
int u , v;
cin >> u >> v;
edge[u].pb (v);
edge[v].pb (u);
}
solve (1, n, 0, n);
for (int i = 1 ; i <= n ; i ++)
cout << ans[i] << endl;
}