题解

~ 2025-10-29 17:21:05

送信卒

Subtask1, 2

首先我们将一条路径用 (w,h)(w, h) 表示,意为这条路径有 ww 次水平移动,hh 次垂直移动。

考虑将所有从 SSTT 的可能形成最短路径的路(即找不到另一条路 w,hw, h 都不大于它)都找出来。计算每条路为最短路时的 kk 值,取最大的即可。

Subtask3

注意到 kk 越大,最短路的长度肯定不减,所以 kk 值可以二分。每次二分一个 kk,跑最短路 (Dijkstra) 检验即可。时间复杂度为 O((n+m)lognlogs)O((n+m) \log n \cdot \log s)

#include <cstdio>
#include <queue>
#include <tuple>
#include <algorithm>
#define eps (1e-4)
#define mp std::make_tuple
#define ld double
using std::tuple;
using std::priority_queue;
const int N=105;
int n, m, sx, sy, tx, ty;
ld s, k, dis[N][N];
int a[N][N];
priority_queue<tuple<ld, int, int> > q;
inline void upt(int x, int y, ld f)
{
	if(a[x][y]||!x|!y||x>n||y>m||dis[x][y]<=f+eps) return;
	dis[x][y]=f;
	q.push(mp(-f, x, y));
}
void dij(void)
{
	for(int i=1; i<=n; ++i) for(int j=1; j<=m; ++j) dis[i][j]=1e9;
	dis[sx][sy]=0;
	q.push(mp(0, sx, sy));
	while(!q.empty())
	{
		auto t=q.top();
		q.pop();
		int x=std::get<1>(t), y=std::get<2>(t);
		if(-std::get<0>(t)!=dis[x][y]) continue;
		ld d=dis[x][y];
		upt(x, y-1, d+1);
		upt(x, y+1, d+1);
		upt(x-1, y, d+k);
		upt(x+1, y, d+k);
	}
}
bool chk(ld c)
{
	k=c;
	dij();
	return s<=dis[tx][ty]+eps;
}
int main()
{
	scanf("%d%d", &n, &m);
	scanf("%d%d%d%d", &sx, &sy, &tx, &ty);
	for(int i=1; i<=n; ++i) for(int j=1; j<=m; ++j) scanf("%d", a[i]+j);
	scanf("%lf", &s);
	ld l=0, r=s;
	while(r-l>eps)
	{
		ld mid=(l+r)/2;
		if(chk(mid)) r=mid;
		else l=mid;
	}
	printf("%.3lf\n", l+(5e-5));
	return 0;
}

星际联邦

做法比较多,可以直接用 Prim 来做,但最简单的做法仍然是考虑 Borůvka 算法,我们在每一轮需要找到这个点向外到另一个联通块内的最小边。注意到当 ii 固定时,最小边要么是前缀 [1,i)[1, i) 的最大值取到的,要么是 (i,n](i, n] 内的最小值取到的。我们只需要对每个前缀维护最大值,以及和最大值不在同一个联通块内的最大值,就可以快速求出该联通块向外的最小边。总的时间复杂度为 O(nlogn)O(n \log n)

#include <bits/stdc++.h>

const int N = 3e5 + 50;

int x[N], f[N], pre[N][2], suf[N][2];

inline int find(int x) {
	while (x != f[x])
		x = f[x] = f[f[x]];
	return x;
}

void push(int *A, int i, const auto &cmp) {
	if (!i) return;
	int fA0 = find(A[0]);
	int fA1 = find(A[1]);
	int fi = find(i);
	assert(A[0] == 0 || A[1] == 0 || fA0 != fA1);
	if (A[0] == 0 || cmp(x[A[0]], x[i])) {
		if (fi == fA0) {
			A[0] = i;
		}
		else {
			A[1] = A[0];
			A[0] = i;
		}
	}
	else if (A[1] == 0 || cmp(x[A[1]], x[i])) {
		if (fi != fA0) {
			A[1] = i;
		}
	}
}


void push_max(int *A, int i) {
	push(A, i, std::less<int>());
}
void push_min(int *A, int i) {
	push(A, i, std::greater<int>());
}
int take(int *A, int i) {
	if (find(i) != find(A[0]))
		return A[0];
	return A[1];
}
int val(int i, int j) {
	if (i > j) std::swap(i, j);
	return x[j] - x[i];
}

int to[N], to_v[N];
int64_t solve() {
	int n;
	std::cin >> n;
	for (int i = 1; i <= n; ++i) {
		std::cin >> x[i];
		f[i] = i;
	}
	int comp = n;
	int64_t ans = 0;
	while (comp > 1) {
		for (int i = 0; i <= n + 1; ++i) {
			pre[i][1] = pre[i][0] = suf[i][0] = suf[i][1] = 0;
		}
		for (int i = 1; i <= n; ++i) {
			if (i != 1) {
				push_max(pre[i], pre[i - 1][0]);
				push_max(pre[i], pre[i - 1][1]);
			}
			push_max(pre[i], i);
		}
		for (int i = n; i >= 1; --i) {
			if (i != n) {
				push_min(suf[i], suf[i + 1][0]);
				push_min(suf[i], suf[i + 1][1]);
			}
			push_min(suf[i], i);
		}
		for (int i = 1; i <= n; ++i) {
			to[i] = 0;
			to_v[i] = 2e9 + 50;
		}
		for (int i = 1; i <= n; ++i) {
			int pre_max = take(pre[i], i), pv = val(pre_max, i);
			int suf_min = take(suf[i], i), sv = val(suf_min, i);
			int o = find(i);
			assert(find(pre_max) != i);
			assert(find(suf_min) != i);
			if (pre_max && pv < to_v[o]) {
				to_v[o] = pv;
				to[o] = pre_max;
			}
			if (suf_min && sv < to_v[o]) {
				to_v[o] = sv;
				to[o] = suf_min;
			}
		}
		for (int i = 1; i <= n; ++i)
			if (i == find(i) && find(i) != find(to[i])) {
				--comp;
				ans += to_v[i];
				f[find(i)] = find(to[i]);
			}
	}
	return ans;
}

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);
	int t=1;
	while (t--) {
		std::cout << solve() << '\n';
	}
}

对称旅行者

考虑先求旅行者 ii 的期望位置,设为 fif_i,那么答案就为 fi2mKf_i * 2^{m K}

当旅行者 ii 旅行时时,由于期望的线性性,$f_i \longleftarrow \frac{1}{2}\left(2 f_{i-1}-f_i+2 f_{i+1}-f_i\right)=f_{i-1}+f_{i+1}-f_i$,考虑其几何含义,发现是把 fif_i 关于 fi1f_{i-1}fi+1f_{i+1} 的中点对称,如果设 gi=fi+1fig_i=f_{i+1}-f_i,那么跳第 ii 枚棋子相当于交换 gi1g_{i-1}gig_i

因此一轮旅行就对应一个 1n11 \sim n-1 的置换,用类似快速幂的方法就可以求出 KK 轮旅行后的 {gi}\left\{g_i\right\},再注意到 f1f_1 始终不变,就可以求出所有棋子的期望位置,时间复杂度为 O(nlogK)O(n \log K)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7,N=1e5+10,iv=(mod+1)/2;
inline int ksm(int x,int y){
	int ret=1;
	for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) ret=1ll*ret*x%mod;
	return ret;
}
inline int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
inline int dec(int x,int y){return (x-y<0)?x-y+mod:x-y;} 
int n,x[N],f[N],m,id[N],vis[N],stk[N],pos[N];
ll k;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&x[i]),f[i]=dec(x[i],x[i-1]),id[i]=i;
	scanf("%d%lld",&m,&k);
	for(int i=1,x;i<=m;++i) scanf("%d",&x),swap(id[x],id[x+1]);
	int del=ksm(2,1ll*m*(k%(mod-1))%(mod-1));
	for(int i=1;i<=n;++i){
		if(!vis[i]){
			int now=i,top=0;
			while(!vis[now]){
				vis[now]=1;stk[top++]=now;
				now=id[now];
			}
			for(int i=0;i<top;++i) x[stk[i]]=f[stk[(i+k)%top]];
		}
	}
	for(int i=1;i<=n;++i) x[i]=add(x[i-1],x[i]),printf("%d ",1ll*x[i]*del%mod);
	return 0;
}

和平精英

首先考虑枚举按位与和按位或的值,假设结果是 valval,那么显然所有 ai<vala_i<val 都应该放到 or 集合里,所有 ai>vala_i>val 都应该放到 and 集合里,如果暴力 check 可以做到 O(nQ)O(nQ),大概有 50pts。

考虑再发掘一些性质,考虑枚举 valval 的 popcount,设为 pp,那么显然所有 popcount(ai)<ppopcount(a_i)<p 都应该放到 or 集合里,所有 popcount(ai)>ppopcount(a_i)>p 都应该放到 and 集合里,popcount(ai)=ppopcount(a_i)=p 的把上面的结论拉下来发现这些数应当全部相等,如果暴力 check 可以做到 O(nQlogv)O(nQ \log v),大概有 25pts。

然后考虑直接离线分治回答询问,可以轻松做到 O(nlog2n)O(n\log^2 n),如果你不幸写了一个根号复杂度,也可以获得多于暴力的分数。

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
inline char nc(){
	static char buf[500005],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,500000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
	char ch=nc(); int sum=0;
	for(;ch<'0'||ch>'9';ch=nc());
	while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
	return sum;
}
int n,Q,a[maxn],pc[maxn]; bool ans[maxn];
struct node { int l,r,id; } q[maxn],T[maxn];
int A[maxn][31],O[maxn][31],c[maxn][31];
int ad[31],o[31],C[31];
void solve(int l,int r,int x,int y) {
	if(l>=r||x>y) return ; int mid=l+r>>1;
	for(int i=l;i<=r;i++) for(int j=0;j<=30;j++) A[i][j]=0x3fffffff,O[i][j]=c[i][j]=0;
	for(int i=mid+1;i<=r;i++) A[i][pc[i]]&=a[i],O[i][pc[i]]|=a[i],c[i][pc[i]]++,memcpy(A[i+1],A[i],124),memcpy(O[i+1],O[i],124),memcpy(c[i+1],c[i],124);
	for(int i=mid;i>=l;i--) A[i][pc[i]]&=a[i],O[i][pc[i]]|=a[i],c[i][pc[i]]++,memcpy(A[i-1],A[i],124),memcpy(O[i-1],O[i],124),memcpy(c[i-1],c[i],124);
	int pl=x-1,pr=y+1;
	for(int _=x;_<=y;_++) {
		if(q[_].r<=mid) T[++pl]=q[_]; else if(q[_].l>mid) T[--pr]=q[_];
		else {
			for(int i=0;i<=30;i++) ad[i]=A[q[_].l][i]&A[q[_].r][i],o[i]=O[q[_].r][i]|O[q[_].l][i],C[i]=c[q[_].l][i]+c[q[_].r][i];
			for(int i=0;i<=30;i++) if(ad[i]>=o[i]) {
				int L=0,R=0x3fffffff,cl=0,cr=0; for(int j=0;j<i;j++) L|=o[j],cl+=C[j]; for(int j=30;j>i;j--) R&=ad[j],cr+=C[j];
				ans[q[_].id]|=(C[i]==0)?(cl&&cr&&L==R):((C[i]==1)?((cl&&(L==(R&o[i])))||(cr&&(R==(L|o[i])))):((L|o[i])==(R&o[i])));
			}
		}
	} for(int i=x;i<=pl;i++) q[i]=T[i]; for(int i=pr;i<=y;i++) q[i]=T[i]; solve(l,mid,x,pl); solve(mid+1,r,pr,y);
}
int main() {
	n=read(),Q=read();
	for(int i=1;i<=n;i++) pc[i]=__builtin_popcount(a[i]=read());
	for(int i=1;i<=Q;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
	solve(1,n,1,Q);
	for(int i=1;i<=Q;i++) puts(ans[i]?"YES":"NO");
}


我们会审查剪贴板内容,并对发布不合适内容的同学进行相应的处理