25/1/18 模拟赛做题记录

B

题意

给定一个序列 \(a_{1\cdots n}\),你需要回答 \(m\) 个询问,每个询问给定 \(l,r\),你需要回答满足 \(l\leq i< j\leq r,a_i=a_j\) 的最小的 \(j-i\),即区间内最近的两个相同数的距离,若不存在,输出 -1

思路

我们设 \(b_i\) 表示与位置 \(i\) 的元素权值相等且在 \(i\) 之后的最小位置。

因为答案要求 \([l,r]\) 之间的最小距离,所以相当于求 \(\min_{l\le i\le r}\{b_i|(i+b_i\le r)\}\)。我们发现 \((i+b_i\le r)\) 并不好控制,而 \(l\le i\le r\) 却是很简单的,所以不妨离线询问,并将其按照右端点排序。之后遍历他们的所有询问,每一次询问前先将 \(i+b_i\le r\) 的所有数加入线段树的位置 \(i\),每一次求 \([l,r]\) 的最小值。

时间复杂度 \(O(n \log n)\)

点击查看代码
#include<bits/stdc++.h>

#define int ll
#define ll long long
#define i128 __int128

#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define fst first 
#define scd second 
#define dbg puts("IAKIOI")

using namespace std;

int read() {
	int x=0,f=1; char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); 
	for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }

const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }

#define maxn 1000050

int n,m;
int a[maxn];
map<int,int> mp;

struct node{
	int idx,l,r;
	bool operator<(const node&x) const {
		return r<x.r;
	}
}b[maxn],qu[maxn];

struct SegT {
	int tr[maxn<<2];
	void pre() {For(i,0,(maxn<<2)-1) tr[i]=1e18; }
	void pu(int idx) {tr[idx]=min(tr[lc(idx)],tr[rc(idx)]);}
	void add(int idx,int l,int r,int k,int val) {
		if(l==r) return tr[idx]=val,void();
		int mid=l+r>>1;
		if(k<=mid) add(lc(idx),l,mid,k,val);
		else add(rc(idx),mid+1,r,k,val);
		pu(idx);
	}
	int query(int idx,int l,int r,int L,int R) {
		int res=1e18;
		if(L<=l&&r<=R) return tr[idx];
		int mid=l+r>>1;
		if(L<=mid) res=min(res,query(lc(idx),l,mid,L,R));
		if(R>mid) res=min(res,query(rc(idx),mid+1,r,L,R));
		return res;
	}
}Tr;

int ans[maxn];

void work() {
	in2(n,m);
	For(i,1,n) {
		in1(a[i]);
		if(mp[a[i]]) {
			b[mp[a[i]]]={mp[a[i]],mp[a[i]],i};
		}
		mp[a[i]]=i;
	}
	For(i,1,n) if(!b[i].idx) {
		b[i].idx=i,b[i].r=1e18;
	}
	For(i,1,m) {
		in2(qu[i].l,qu[i].r);
		qu[i].idx=i;
	}
	sort(qu+1,qu+m+1);
	sort(b+1,b+n+1);
	int idx=0;
	Tr.pre();
	For(i,1,m) {
		while(idx+1<=n&&b[idx+1].r<=qu[i].r) {
			idx++; Tr.add(1,1,n,b[idx].idx,b[idx].r-b[idx].l);
		}
		ans[qu[i].idx]=Tr.query(1,1,n,qu[i].l,qu[i].r);
		if(ans[qu[i].idx]>=1e18) ans[qu[i].idx]=-1;
	}
	For(i,1,m) cout<<ans[i]<<'\n';
}

signed main() {
//	ios::sync_with_stdio(false); 
//	cin.tie(0); cout.tie(0);
	int _=1;
//	_=read();
//	cin>>_;
	For(i,1,_) {
		work();
	}
	return 0;
}

C

题意

原题链接:CF1508B

如果有一个 \(1\)\(n\) 的排列 \(p_{1\cdots n}\) 满足对于所有 \(1\leq i< n\) 都有 \(p_i-1\leq p_{i+1}\),则我们称排列 \(p\) 是「几乎有序」的。
给定 \(n,k\),你需要求出字典序第 \(k\) 小的「几乎有序」的长度为 \(n\) 的排列或报告其不存在。

思路

首先,题目给出的 \(a_i-1\le a_{i+1}\),这意味着如果排列中有下降的子序列,一定形如:\(\{a,a-1,a-2,\cdots,a-x,b,b-1,b-2,\cdots\}\)
所以题目变成给序列化分成若干段,每一段反转后得到的第 \(k\) 小,总共只有 \(2^{n-1}\) 种情况。
首先,我们知道最小肯定是 \(\{1,2,3,\cdots,n\}\)。我们发现每一次反转越靠后,改变的字典序就越大。所以我们不难发现,当一个间隙切割时为 \(0\),不切割时为 \(1\),那么从左往右的 \(01\) 串组成的二进制数 \(v\),构造出的序列的字典序大小是 \(v+1\)
另:因为 \(2^{62}\ge 10^{18}\),所以只要 \(n\ge 62\) 就一定有解。

点击查看代码
#include<bits/stdc++.h>
#define int ll
#define ll long long
#define i128 __int128

#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define fst first 
#define scd second 
#define dbg puts("IAKIOI")

using namespace std;

int read() {
	int x=0,f=1; char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); 
	for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }

const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }

#define maxn 200050

int n,k;

void work() {
	in2(n,k); k--;
	if(n<=62&&(k>>(n-1))) return puts("-1"),void();
	for(int l=1,r=1;l<=n;l=r+1,r++) {
		while(r<n&&n-r<=62&&((k>>(n-1-r))&1)) r++;//n-r<62 是因为最后一个 0 的位置和位置 n 的距离不会超过 62,因为 2^62>10^18
		Rep(j,r,l) cout<<j<<' ';
	}
	puts("");
}

signed main() {
//	ios::sync_with_stdio(false); 
//	cin.tie(0); cout.tie(0);
	int _=1;
//	_=read();
//	cin>>_;
	For(i,1,_) {
		work();
	}
	return 0;
}

D

题意

给定序列 \(a_{1\cdots n}\) 以及 \(m\) 个询问,每次询问给定 \(l,r,p\),求: $$ \min_{l\leq L\leq R\leq r}{(\sum_{i=L}^R a_i)\bmod p} $$ 即询问区间 \([l,r]\) 的所有子区间和在模意义下的最小值,注意先取模再求最小值。

思路

我们设 \(sum_i=\sum_{i=l}^{i}\)。首先对于每一次询问,如果 \(l-r+1>p\),那么根据鸽巢原理,一定有两个相同的数。否则我们遍历区间,设当前遍历到 \(i\),我们要找到最大的小于等于 \(sum_i\)\(sum_j(j\ne i)\)。用权值树状数组维护前缀最大值即可。时间复杂度看上去是 \(O(nm\log p)\),实际上是 \(O(mp\log p)\),原因是每一次遍历的长度大小不超过 \(p\)

点击查看代码
#include<bits/stdc++.h>
#define int ll
#define ll long long
#define i128 __int128

#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define fst first 
#define scd second 
#define dbg puts("IAKIOI")

using namespace std;

int read() {
	int x=0,f=1; char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); 
	for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }

const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }

#define maxn 500050

struct Bit {
	int c[550];
	void add(int x,int val) {
		for(;x<550;x+=lb(x)) c[x]=max(c[x],val);
	}
	int query(int x) {
		int res=0;
		for(;x;x-=lb(x)) res=max(res,c[x]);
		return res;
	}
}Tr;

int n,m;
int a[maxn],sum[maxn];

void work() {
	in2(n,m);
	For(i,1,n) in1(a[i]),sum[i]=sum[i-1]+a[i];
	For(i,1,m) {
		int l=read(),r=read(),p=read();
		if(l-r+1>p) { cout<<0<<'\n'; continue; }
		For(i,0,p) Tr.c[i]=0;
		int ans=1e18;
		For(j,l,r) {
			int res=(sum[j]-sum[l-1])%p;
			ans=min(ans,res); 
			ans=min(ans,res-Tr.query(res));
			if(ans==0) break;
			Tr.add(res,res);
		}
		cout<<ans<<'\n';
	}
}

signed main() {
//	ios::sync_with_stdio(false); 
//	cin.tie(0); cout.tie(0);
	int _=1;
//	_=read();
//	cin>>_;
	For(i,1,_) {
		work();
	}
	return 0;
}
posted @ 2025-01-19 14:48  coding_goat_qwq  阅读(21)  评论(0)    收藏  举报