Loading

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;
}

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

T4

妙!

这个操作是可逆的:也就是说,我们执行一些操作让 \(a,b\) 都有序,那么可以把这些操作都堆到 \(a\) 上,那么 \(a,b\) 就相等了。

显然我们可以进行序列长度次操作,来翻转一个有序的序列。

另一个操作是:对于两段值域不交的序列,我们可以用线性次操作将它们归并起来。

一种 naive 的做法是分块:假如每块大小为 \(S\),那么我们可以从前往后处理这些块。假如处理到第 \(i\) 块,那么它需要 \(S^2\) 个操作排好序,然后需要 \(iS\) 个操作归并到前面排好序的序列中:设前面排好序的序列是 \(a_1,a_2,\dots,a_x\),当前块内排好序的序列是 \(b_1,b_2,\dots,b_y\),那么第一次我们将 \(b_m\) 归并到 \(a\) 中恰好小于它的位置的后面(利用上面的操作),形成了 \(a_1,a_2,\dots,a_k,b_1,\dots,b_m,a_{k+1},\dots,a_x\),然后递归到 \(a_1\dots b_{m-1}\) 即可。于是总操作次数 \(=\sum_{i=1}^{n/S} (iS+S^2)\),当 \(S=\sqrt{n}\)\(=\mathcal{O}(n\sqrt{n})\)

考虑归并排序,难点在于如何归并两段有序的序列。考虑分治搞这个东西:设原先的序列是 \(A=\langle a_1,\dots,a_x,b_1,\dots,b_y\rangle\)\(\forall i<j,a_i<a_j\land b_i<b_j\)。我们取一个数 \(mid\) 使得 \(A\)\(\le mid\) 的数的个数等于 \(A\)\(>mid\) 的个数,于是把 \(A\) 分成了四部分:

  1. \(a\)\(\le mid\) 的数;
  2. \(a\)\(>mid\) 的数;
  3. \(b\)\(\le mid\) 的数;
  4. \(b\)\(>mid\) 的数。

因为 \(2,3\) 部分值域不交,所以我们可以把它们用线性次操作归并起来,然后就成了一个前缀 \(\le mid\),剩下的 \(>mid\),递归到这两个子问题即可。

于是操作次数是 \(\mathcal{O}(n\log^2 n)\) 的。

posted @ 2021-11-18 12:23  Alan_Zhao_2007  阅读(60)  评论(0编辑  收藏  举报