[每日随题12] BFS- 树上背包 - 动态开点线段树

整体概述

  • 难度:1500 \(\rightarrow\) 2000 \(\rightarrow\) 2200

P3918 [国家集训队] 特技飞行

  • 标签:BFS

  • 前置知识:无

  • 难度:黄 1500

题目描述:

image

输入格式:

image

image

输出格式:

image

样例输入:

2
1 2 3
4 5 6
7 8 9
1 5 2
4 6 3
7 8 9

样例输出:

0
3

解题思路:

  • 本题如果从每一个起点开始 bfs 到终点,在 \(10^5\) 次询问下会超时。

  • 我们发现 \(9!\) 仅为 \(362,880\),我们考虑从终点预处理出到所有可能情况的步数,最后直接 \(O(1)\) 查询。

  • 我们用 \(string\) 来存矩阵,注意从终点出发需要逆时针旋转,跑一遍 \(bfs\) 即可。

完整代码

#include<bits/stdc++.h>
#define P(x,y) ((x)*3+(y))
using namespace std;
const int N = 9*8*7*6*5*4*3*2 + 5;
unordered_map<string,int> mp;
inline string ring(string str,int x,int y){
	char tmp = str[P(x,y)];
	str[P(x,y)] = str[P(x,y+1)], str[P(x,y+1)] = str[P(x+1,y+1)];
	str[P(x+1,y+1)] = str[P(x+1,y)], str[P(x+1,y)] = tmp;
	return str;
}
struct Node{string s;int step;}; queue<Node> qu; 
int ans[N]; bool vis[N];
inline void bfs(string st){
	qu.push({st,0}), mp[st] = mp.size()+1;
	while(!qu.empty()){
		Node cur = qu.front(); qu.pop();
		string s = cur.s; int step = cur.step;
		for(int x=0;x<=1;x++) for(int y=0;y<=1;y++){
			string ns = ring(s,x,y);
			if(mp.find(ns) == mp.end()){
				mp[ns] = mp.size()+1;
				ans[mp[ns]] = step+1;
				qu.push({ns,step+1});
			}
		}
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	bfs("123456789");
	int T; cin >> T;
	while(T--){
		string str = "";
		for(char i=1,ch;i<=9;i++) cin >> ch, str += ch;
		cout << ans[mp[str]] << '\n';
	}
	return 0;
}

P4516 [JSOI2018] 潜入行动

  • 标签:树上背包

  • 前置知识:背包\(DP\),树型\(DP\)

  • 难度:蓝 2000

题目描述:

image

输入格式:

image

image

输出格式:

image

样例输入:

5 3
1 2
2 3
3 4
4 5

样例输出:

1

解题思路:

  • 由于每个点都可以放置监听设备,且不监听该点,则我们需要记录该点是否放了监听器,以及该点是否被监听。

  • 那么我们定义 \(dp_{u,k,p,q}\) 表示以 \(u\) 为根的子树用了 \(k\) 个监听器,下方所有节点均被监听,节点 \(u\) 是否放置监听器为 \(p\),是否被监听为 \(q\),状态下的总方案数。

  • 我们发现背包每次合并需要 \(O(k^2)\) 的复杂度,而总共有 \(n\) 个节点,最大合并次数不会超过 \(\frac {n} {k}\) 次,那么总复杂度为 \(O(nk)\) 不会超时。

  • 接着考虑如何状态转移,对于当前节点 \(u\),某个子节点 \(v\),我们暴力枚举 \(u\) 已经用了 \(a\) 个监听器,\(v\) 子树额外用了 \(b\) 个监听器,\(u\) 节点的状态为 \(p1\)\(q1\)\(v\) 节点的状态为 \(p2\)\(q2\),此时可以更新哪个节点。

    我们需要 \(v\) 节点被监听,那么 \(p1|q2\) 需要为 \(1\),在此条件下可以更新节点 \(dp_{u,a+b,p1,p2|q1}\)

  • 那么全部更新一遍,最后 \(dp_{1,K,0,1} + dp_{1,K,1,1}\) 即是答案。

完整代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int N = 1e5+5,M = 105, mod = 1e9+7;
int n,K,ha[N],idx;
struct Edge{int to,ne;}edge[N<<1];
inline void ins(int u,int v){
	edge[++idx] = {v,ha[u]}, ha[u] = idx;
}
int dp[N][M][2][2],tmp[M][2][2],maxK[N];
inline void dfs(int u,int par){
	maxK[u] = 1;
	dp[u][0][0][0] = dp[u][1][1][0] = 1;
	for(int i=ha[u];i;i=edge[i].ne){
		int v = edge[i].to;
		if(v == par) continue;
		dfs(v,u);
		int up = min(K,maxK[u] + maxK[v]);
		rep(a,0,maxK[u]) rep(b,0,min(maxK[v],K-a))
			rep(p1,0,1) rep(q1,0,1) rep(p2,0,1) rep(q2,0,1)
				if(p1 | q2) tmp[a+b][p1][p2|q1] = (tmp[a+b][p1][p2|q1] + 1ll*dp[u][a][p1][q1]*dp[v][b][p2][q2]%mod)%mod;
		rep(k,0,up) rep(p,0,1) rep(q,0,1)
			dp[u][k][p][q] = tmp[k][p][q], tmp[k][p][q] = 0;
		maxK[u] = up;
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin >> n >> K;
	for(int i=1,u,v;i<n;i++){
		cin >> u >> v;
		ins(u,v), ins(v,u);
	}
	dfs(1,0);
	cout << (dp[1][K][0][1] + dp[1][K][1][1])%mod;
	return 0;
}

P5848 [IOI 2005] mou

  • 标签:动态开点线段树

  • 前置知识:线段树

  • 难度:蓝 2200

题目描述:

image

输入格式:

image

image

输出格式:

image

样例输入:

4
Q 1
I 1 4 2
Q 3
Q 1
I 2 2 -1
Q 3
E

样例输出:

4
1
0
3

解题思路:

  • 读完题目,发现所求很简单,一个支持区间修改为同一个数,查询满足 \(h\lt sum(1,x)\) 的最小的 \(x\)

  • 发现数据中可以把高度改为负数,那么我们不只需要记录区间和 \(sm\) 还需要记录区间前缀和的最大值 \(mx\),查询时在线段树上分治即可。

  • 需要注意的是,下边 \(n\le 10^9\) 我们需要动态开点,总复杂度 \(O(q·log_{2}{(10^9)})\)

完整代码

#include<bits/stdc++.h>
#define mid (((l)+(r))>>1)
using namespace std;
const int Q = 1e5,INF = 0x3f3f3f3f;
int n,ls[Q<<6],rs[Q<<6],sum[Q<<6],pmx[Q<<6],tag[Q<<6],tot,rt;
inline void up(int u){
	sum[u] = sum[ls[u]] + sum[rs[u]];
	pmx[u] = max(pmx[ls[u]],sum[ls[u]] + pmx[rs[u]]);
}
inline void lazy(int v,int &u,int l,int r){
	if(!u) u = ++tot;
	sum[u] = (r-l+1)*v;
	pmx[u] = max(0,sum[u]);
	tag[u] = v;
}
inline void down(int &u,int l,int r){
	if(tag[u] != -INF){
		lazy(tag[u],ls[u],l,mid);
		lazy(tag[u],rs[u],mid+1,r);
		tag[u] = -INF;
	}
}
inline void update(int x,int y,int v,int &u,int l=1,int r=n){
	if(!u) tag[u = ++tot] = -INF;
	if(x <= l && r <= y){ lazy(v,u,l,r); return; } 
	down(u,l,r);
	if(x <= mid) update(x,y,v,ls[u],l,mid);
	if(y > mid) update(x,y,v,rs[u],mid+1,r);
	up(u);
}
inline int query(int h,int &u,int l=1,int r=n){ 
	if(l == r) return h >= sum[u] ? l : l-1;  
	down(u,l,r);
	if(pmx[ls[u]] <= h) return query(h-sum[ls[u]],rs[u],mid+1,r);
	return query(h,ls[u],l,mid);
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin >> n; 
	update(1,n,0,rt);
	char op; cin >> op;
	while(op != 'E'){
		if(op == 'Q'){
			int h; cin >> h;
			cout << query(h,rt) << '\n';
		}else{
			int l,r,v; cin >> l >> r >> v;
			update(l,r,v,rt);
		}
		cin >> op;
	}
	return 0;
}

posted @ 2025-07-20 09:00  浅叶梦缘  阅读(10)  评论(0)    收藏  举报