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\) 分成了四部分:
- \(a\) 中 \(\le mid\) 的数;
- \(a\) 中 \(>mid\) 的数;
- \(b\) 中 \(\le mid\) 的数;
- \(b\) 中 \(>mid\) 的数。
因为 \(2,3\) 部分值域不交,所以我们可以把它们用线性次操作归并起来,然后就成了一个前缀 \(\le mid\),剩下的 \(>mid\),递归到这两个子问题即可。
于是操作次数是 \(\mathcal{O}(n\log^2 n)\) 的。