分治算法专题训练二
lgP4178 Tree
题意:
sol.点分治板子题
#include<bits/stdc++.h>
#define MAXN 50005
#define INF 0x3f3f3f3f
using namespace std;
int n,K,h[MAXN],tot;
struct node{int from,to,next,cost;}e[MAXN << 1];
void add(int x , int y , int z){
tot++;
e[tot].from = x;
e[tot].to = y;
e[tot].cost = z;
e[tot].next = h[x];
h[x] = tot;
}
int sz[MAXN],val[MAXN],vis[MAXN],zx,dis[MAXN];
int js,jjs,dui[MAXN],in[10000005],ans[MAXN];
void get_sz(int now , int fa){
sz[now] = 1;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
get_sz(e[i].to , now);
sz[now] += sz[e[i].to];
}
}
void get_zx(int now , int fa , int yl){
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
get_zx(e[i].to , now , yl);
val[now] = max(val[now] , sz[e[i].to]);
}
val[now] = max(val[now] , sz[yl] - sz[now]);
if(val[zx] > val[now])zx = now;
}
void get_dis(int now , int fa){
if(dis[now] <= K)dui[++js] = dis[now];
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
dis[e[i].to] = dis[now] + e[i].cost;
get_dis(e[i].to , now);
}
}
void cal(int now){
js = jjs = 0;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(vis[e[i].to])continue;
jjs = js;
dis[e[i].to] = e[i].cost;
get_dis(e[i].to , now);
for(int j = 1 ; j <= K ; j++){
if(!in[j])continue;
for(int p = jjs + 1 ; p <= js ; p++){
if(j + dui[p] > K)continue;
ans[j + dui[p]] += in[j];
}
}
for(int p = jjs + 1 ; p <= js ; p++)in[dui[p]]++;
}
for(int i = 1 ; i <= js ; i++)ans[dui[i]]++;
for(int i = 1 ; i <= js ; i++)in[dui[i]] = 0;
}
void solve(int now){
cal(now) , vis[now] = 1;
get_sz(now , now);
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(vis[e[i].to])continue;
zx = 0 , get_zx(e[i].to , now , e[i].to);
solve(zx);
}
}
int main(){
val[0] = INF;
memset(h , -1 , sizeof(h)) , tot = 0;
scanf("%d" , &n);int x,y,z;
for(int i = 1 ; i < n ; i++){
scanf("%d%d%d" , &x , &y , &z);
add(x , y , z) , add(y , x , z);
}
scanf("%d" , &K);
zx = 0;
get_sz(1 , 1);
get_zx(1 , 1 , 1);
solve(zx);
int sum = 0;
for(int i = 1 ; i <= K ; i++)sum += ans[i];
cout<<sum<<endl;
}
lgP2634
题意:给你一颗带边权的树。询问有多少对点的路径长度是3的倍数
sol.点分治板子。打着练一下手
#include<bits/stdc++.h>
#define MAXN 40005
#define INF 0x3f3f3f3f
using namespace std;
int n,h[MAXN],tot,ans;
struct node{int from,to,next,cost;}e[MAXN << 1];
void add(int x , int y , int z){
tot++;
e[tot].from = x;
e[tot].to = y;
e[tot].cost = z;
e[tot].next = h[x];
h[x] = tot;
}
int val[MAXN],sz[MAXN],zx,vis[MAXN];
int dis[MAXN],cnt[5],cnt2[5];
void get_sz(int now , int fa){
sz[now] = 1;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
get_sz(e[i].to , now);
sz[now] += sz[e[i].to];
}
}
void get_zx(int now , int fa , int yl){
val[now] = 0;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
get_zx(e[i].to , now , yl);
val[now] = max(val[now] , sz[e[i].to]);
}
val[now] = max(val[now] , sz[yl] - sz[now]);
if(val[zx] > val[now])zx = now;
}
void get_dis(int now , int fa){
cnt2[dis[now] % 3]++;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
dis[e[i].to] = dis[now] + e[i].cost;
get_dis(e[i].to , now);
}
}
void cal(int now){
for(int i = 0 ; i <= 2 ; i++)cnt[i] = cnt2[i] = 0;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(vis[e[i].to])continue;
for(int j = 0 ; j <= 2 ; j++)cnt2[j] = 0;
dis[e[i].to] = e[i].cost;
get_dis(e[i].to , now);
for(int j = 1 ; j <= 2 ; j++)ans = ans + cnt[3 - j] * cnt2[j];
ans = ans + cnt2[0] * cnt[0];
for(int j = 0 ; j <= 2 ; j++)cnt[j] += cnt2[j];
}
ans = ans + cnt[0];
}
void solve(int now){
vis[now] = 1;
cal(now);
get_sz(now , now);
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(vis[e[i].to])continue;
zx = 0 , get_zx(e[i].to , now , e[i].to);
solve(zx);
}
}
int gcd(int a , int b){
if(!b)return a;
return gcd(b , a % b);
}
namespace io {
const int SIZE = 1 << 22 | 1;
char iBuf[SIZE], *iS, *iT, c;
char oBuf[SIZE], *oS = oBuf, *oT = oBuf + SIZE;
#define gc() (iS == iT ? iT = iBuf + fread(iS = iBuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS++) : *iS++)
template<class I> void read(I &x) {
int f = 1;
for(c = gc(); c < '0' || c > '9'; c = gc())
if(c == '-') f = -1;
for(x = 0; c >= '0' && c <= '9'; c = gc())
x = (x << 3) + (x << 1) + (c & 15);
x *= f;
}
inline void flush () {
fwrite(oBuf, 1, oS - oBuf, stdout);
oS = oBuf;
}
inline void putc(char x) {
*oS++ = x;
if(oS == oT) flush();
}
template<class I> void print(I x) {
if(x < 0) putc('-'), x = -x;
static char qu[55];
char *tmp = qu;
do *tmp++ = (x % 10) ^ '0'; while(x /= 10);
while(tmp-- != qu) putc(*tmp);
putc('\n');
}
struct flusher{ ~flusher() { flush(); } }_;
}
int main(){
memset(h , -1 , sizeof(h)) , tot = 0 , val[0] = INF;
int x,y,z;
io :: read(n);
for(int i = 1 ; i < n ; i++){
io :: read(x);
io :: read(y);
io :: read(z);
add(x , y , z);
add(y , x , z);
}
get_sz(1 , 1);
zx = 0 , get_zx(1 , 1 , 1);
solve(zx);
int zz = gcd(ans * 2 + n , n * n);
cout<<(ans * 2 + n) / zz<<"/"<<n * n / zz<<endl;
}
lgP4149 [IOI2011]Race
题意:
sol.点分治板子。打着练一下手
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAXN 5000005
using namespace std;
int n,K,ans = INF;
int h[MAXN],tot;
struct node{int from,to,next,cost;}e[MAXN << 1];
void add(int x , int y , int z){
tot++;
e[tot].from = x;
e[tot].to = y;
e[tot].cost = z;
e[tot].next = h[x];
h[x] = tot;
}
int vis[MAXN],dis[MAXN],sum[MAXN];
int sz[MAXN],val[MAXN],zx;
void get_sz(int now , int fa){
sz[now] = 1;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
get_sz(e[i].to , now);
sz[now] += sz[e[i].to];
}
}
void get_zx(int now , int fa , int yl){
val[now] = 0;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
get_zx(e[i].to , now , yl);
val[now] = max(val[now] , sz[e[i].to]);
}
val[now] = max(val[now] , sz[yl] - sz[now]);
if(val[now] < val[zx])zx = now;
}
int len;
int q[MAXN],in[MAXN];
void get_dis(int now , int fa){
if(dis[now] <= K)len++ , q[len] = now;
if(dis[now] == K && ans > sum[now]){
ans = min(ans , sum[now]);
}
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(e[i].to == fa)continue;
if(vis[e[i].to])continue;
dis[e[i].to] = dis[now] + e[i].cost;
sum[e[i].to] = sum[now] + 1;
get_dis(e[i].to , now);
}
}
void cal(int now){
int zz;len = 0;
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(vis[e[i].to])continue;
zz = len , dis[e[i].to] = e[i].cost , sum[e[i].to] = 1;
get_dis(e[i].to , now);
for(int j = zz + 1 ; j <= len ; j++)ans = min(ans , in[K - dis[q[j]]] + sum[q[j]]);
for(int j = zz + 1 ; j <= len ; j++)in[dis[q[j]]] = min(in[dis[q[j]]] , sum[q[j]]);
}
for(int i = 1 ; i <= len ; i++)in[dis[q[i]]] = INF;
}
void solve(int now){
vis[now] = 1;
cal(now);
get_sz(now , now);
for(int i = h[now] ; i != (-1) ; i = e[i].next){
if(vis[e[i].to])continue;
zx = 0 , get_zx(e[i].to , now , e[i].to);
solve(zx);
}
}
int main(){
val[0] = INF;
memset(in , 0x3f , sizeof(in));
memset(h , -1 , sizeof(h)) , tot = 0;
scanf("%d%d" , &n , &K);int x,y,z;
for(int i = 1 ; i < n ; i++){
scanf("%d%d%d" , &x , &y , &z) , x++ , y++;
add(x , y , z) , add(y , x , z);
}
get_sz(1 , 1);
zx = 0 , get_zx(1 , 1 , 1);
solve(zx);
if(ans == INF)cout<<-1<<endl;
else cout<<ans<<endl;
}
BZOJ3697
题意:给出一棵树,边权0/1。
称一条路是“平衡”的,其中0边和1边数量相同。
现在询问有多少条边“双平衡路径”,即存在一个中转点,从起点到中转点,从中转点到终点的路径都是“平衡的” ,
sol.就点分值维护一个东西吧,(由于没地方交,我就嘴巴写写吧)
cf448C
直接就是一个分治裸题
#include<bits/stdc++.h>
#define MAXN 5005
#define INF 0x3f3f3f3f
using namespace std;
int n,h[MAXN];
int minl[MAXN][25],lg[MAXN];
int que(int l , int r){
int zz = lg[r - l + 1];
if(h[minl[l][zz]] < h[minl[r - (1 << zz) + 1][zz]])return minl[l][zz];
else return minl[r - (1 << zz) + 1][zz];
}
int solve(int l , int r , int v){
if(l > r)return 0;
if(l == r)return v != h[l];
int zz1 = INF , zz2 = 0;
int mid = que(l , r);
return min(solve(l , mid - 1 , h[mid]) + solve(mid + 1 , r , h[mid]) + h[mid] - v , r - l + 1);
}
int main(){
//freopen("out.txt" , "r" , stdin);
scanf("%d" , &n);for(int i = 1 ; i <= n ; i++)scanf("%d" , &h[i]);
lg[0] = (-1);for(int i = 1 ; i <= n ; i++)lg[i] = lg[i >> 1] + 1;
for(int i = 1 ; i <= n ; i++)minl[i][0] = i;
for(int j = 1 ; j <= 20 ; j++){
for(int i = 1 ; i + (1 << j) - 1 <= n ; i++){
if(h[minl[i][j - 1]] <= h[minl[i + (1 << (j - 1))][j - 1]])minl[i][j] = minl[i][j - 1];
else minl[i][j] = minl[i + (1 << (j - 1))][j - 1];
}
}
cout<<solve(1 , n , 0)<<endl;
}
也有的贪心暴力做法,不过也就那样。。。。好像还是noip2018T1原题
CF475D
题意:给你一个长度为的序列,一个询问,一次询问让你求 区间的个数,使得
sol.考虑你 固定左端点,移动右端点的整个过程里, gcd只会发生log次变化
考虑如何处理这个问题
不妨将所有询问离线下来,然后对于原来的序列 有整一个区间中点mid
考虑 每个区间都经过这个区间中间的答案,每次统计因为 gcd只会发生log次变化,可以直接开个桶,直接做就好了
复杂度大概是的
#include<bits/stdc++.h>
#define MAXN 300005
#define INF 0x3f3f3f3f
using namespace std;
int gcd(int x , int y){
if(!y)return x;
return gcd(y , x % y);
}
int n,a[MAXN],b[MAXN],Q;
int len1,len2;
struct node{int val,num;}t1[MAXN],t2[MAXN];
map<int , int>ans;
void solve(int l , int r){
if(r < l)return;
int mid = (l + r) >> 1;
b[mid] = a[mid];
for(int i = mid - 1 ; i >= l ; i--)b[i] = gcd(a[i] , b[i + 1]);
for(int i = mid + 1 ; i <= r ; i++)b[i] = gcd(a[i] , b[i - 1]);
len1 = len2 = 0;
for(int i = mid ; i >= l ; i--){
if(t1[len1].val != b[i])len1++ , t1[len1].val = b[i] , t1[len1].num = 0;
t1[len1].num++;
}
for(int i = mid ; i <= r ; i++){
if(t2[len2].val != b[i])len2++ , t2[len2].val = b[i] , t2[len2].num = 0;
t2[len2].num++;
}
for(int i = 1 ; i <= len1 ; i++){
for(int j = 1 ; j <= len2 ; j++){
ans[gcd(t1[i].val , t2[j].val)] += t1[i].num * t2[j].num;
}
}
solve(l , mid - 1);
solve(mid + 1 , r);
}
int main(){
scanf("%d" , &n);
for(int i = 1 ; i <= n ; i++)scanf("%d" , &a[i]);
solve(1 , n);
//for(int i = 1 ; i <= 10 ; i++)cout<<i<<" "<<ans[i]<<endl;
int x;
scanf("%d" , &Q);
while(Q--){
scanf("%d" , &x);
cout<<ans[x]<<endl;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下