XJOI 11.13

T1

用单调队列求出可以涂色的 \(n-x+1\) 个区间的区间最小值,那么就是每次将一段区间内的数对一个数取 \(\max\),最后询问每个位置是多少。

显然用 set 维护一下就好了。这样形成了若干个连续段,每个段内所有位置被涂色的高度相等。对于一个长度为 \(len\) 的段,至少需要 \(\lceil len/x \rceil\) 次染色才能完成,于是第二问的答案就是 \(\sum_{i} \lceil len_i/x \rceil\)。

T2

可以发现一个位置可以和在它前面的任意多个位置异或,也可以选择不异或。于是维护线性基即可,因为线性基的任意一个子集能表示的数互不相同。

T3

考虑直接 bfs:用 \((u,dis,p)\) 三元组记录当前到达的点、边数和概率。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
typedef long long ll;
typedef pair<int,double> pid;
const int N=5e4+5;
int n,m,dis[N];
double p[N];
vector<pid> g[N];
int main(){
	ios::sync_with_stdio(false),cin.tie(nullptr);
	freopen("wude.in","r",stdin);
	freopen("wude.out","w",stdout);
	cin>>n>>m;
	For(i,1,m){
		int u,v,w;
		cin>>u>>v>>w;
		g[u].emplace_back(v,w/100.),g[v].emplace_back(u,w/100.);
	}
	For(i,1,n) dis[i]=0x3f3f3f3f;
	using Node=struct{int u,dis;double p;};
	queue<Node> q;
	q.push({1,0,1}),p[1]=1,dis[1]=0;
	while(!q.empty()){
		auto _=q.front();q.pop();
		for(auto v_:g[_.u]){
			int v=v_.first;double w=v_.second;
			if(p[v]<_.p*w&&_.p*w>=.75){
				p[v]=_.p*w,dis[v]=min(_.dis+1,dis[v]);
				q.push({v,_.dis+1,p[v]});
			}
		}
	}
	For(i,1,n){
		if(p[i]>=.75) cout<<dis[i]<<' ';
		else cout<<"-1 ";
	}
	return 0;
}

不会证时间复杂度,但可以保证正确性:对于每个点,第一次扩展到它的一定是最短路,第二次一定是次短路,以此类推,可以保证路径最短。

上一篇:shell 运算符; 判断中 if -a 与运算 -o或运算


下一篇:P2058 [NOIP2016 普及组] 队列+桶