[模拟赛]2022.07.25

难度:提高(-)

分数:100+100+100+0


[模拟]T1 旅行日记

n n n 天,其中有 m m m 天有高度 h d i h_{d_i} hdi 表示第 d i d_i di 天高度为 h d i h_{d_i} hdi ,求是否可能保证 ∣ h i − h i − 1 ∣ ≤ 1 |h_{i}-h_{i-1}|\leq 1 hihi11 ,若可能输出最大的可能到达高度。保证 d i < d i + 1 d_i<d_{i+1} di<di+1 n ≤ 1 0 8 , m ≤ 1 0 5 n\leq 10^8 ,m\leq 10^5 n108,m105


真就硬贪,已经排好序了,就直接将 d i d_i di d i + 1 d_{i+1} di+1 贪心求最高,然后判断 I m p o s s i b l e Impossible Impossible 就直接比较 d i + 1 − d i d_{i+1}-d_i di+1di ∣ h d i − h d i + 1 ∣ |h_{d_i}-h_{d_{i+1}}| hdihdi+1 的关系即可。(不放代码)


[二分,单调队列]T2 运动

给定序列 s s s ,求满足 m a x { s i , j } − m i n { s i , j } ≤ k max\{s_{i,j}\}-min\{s_{i,j}\}\leq k max{si,j}min{si,j}k 的最大长度 j − i j-i ji n ≤ 3 × 1 0 6 n\leq 3\times10^6 n3×106 。(时限3s)


没想到 O ( n   l o g   n ) O(n\,log\,n) O(nlogn) 没有被卡掉。首先判断区间的最大最小值可以用单调队列 O ( n ) O(n) O(n) 求出,然后就二分就好了,跑的飞快。

然后是 O ( n ) O(n) O(n) 的正解,其实只需要将思路转换一下,在单调队列中记录最大值和最小值,若 m a x − m i n > k max-min>k maxmin>k p o p pop pop 出队,这样就是 O ( n ) O(n) O(n) 的了。(只有 O ( n   l o g   n ) O(n\,log\,n) O(nlogn) 的代码)。

#include<bits/stdc++.h>
using namespace std;
struct IO{
    inline char read(){
        static const int IN_LEN=1<<18|1;
        static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
    template <typename _Tp> inline IO & operator >> (_Tp&x){
        static char c11,boo;
        for(c11=read(),boo=0;!isdigit(c11);c11=read()){
            if(c11==-1)return *this;
            boo|=c11=='-';
        }
        for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
        boo&&(x=-x);
        return *this;
    }
    inline void push(const char &c) {
    	putchar(c);
	}
	template <class T>
	inline void write(T x){
		if (x < 0) x = -x, push('-');
		static T sta[35];
		T top = 0;
		do {
			sta[top++] = x % 10, x /= 10;
		} while (x);
		while (top) push(sta[--top] + '0');
	}
	template <class T>
	inline void write(T x, char lastChar){
		write(x),push(lastChar);
	}
}io;

int k,n;
int a[3000005];
int maxn[3000005],minn[3000005];

bool solve(int L){
	int l1=1,l2=1,r1=0,r2=0;
	for(int i=1;i<=n;++i){
		while(l1<=r1 && i-maxn[l1]>=L)
			++l1;
		while(l1<=r1 && a[maxn[r1]]<a[i])
			--r1;
		maxn[++r1]=i;
		while(l2<=r2 && i-minn[l2]>=L)
			++l2;
		while(l2<=r2 && a[minn[r2]]>a[i])
			--r2;
		minn[++r2]=i;
		if(i>=L){
			if(a[maxn[l1]]-a[minn[l2]]<=k)
				return 1;
		}
	}
	return 0;
}


int main(){
	io>>k>>n;
	for(int i=1;i<=n;++i)
		io>>a[i];
	int l=1,r=n+1;
	while(l<r){
		int mid=(l+r)>>1;
		int g=solve(mid);
		if(g)
			l=mid+1;
		else
			r=mid;
	}
	printf("%d",l-1);
	return 0;
}


[DP]T3 回文

给定字符串 s s s ,求 s l , r s_{l,r} sl,r 中回文串个数。多组询问, ∣ s ∣ ≤ 5000 |s|\leq 5000 s5000 , T ≤ 1 0 5 T\leq10^5 T105


首先介绍 O ( n × T ) O(n\times T) O(n×T) 的离谱做法(竟然没卡掉),先跑 M a n a c h a r Manachar Manachar ,然后暴力查询 [ l , r ] [l,r] [l,r] 的回文串数量,最后用一个数组记录下来(防1 5000数据)即可。

然后是正解,首先处理出 g [ i ] [ j ] g[i][j] g[i][j] 表示 s i , j s_{i,j} si,j 是否为回文串,然后推出 s i , j s_{i,j} si,j 中回文串总个数 f [ i ] [ j ] = f [ i + 1 ] [ j ] + f [ i ] [ j − 1 ] − f [ i + 1 ] [ j − 1 ] + g [ i ] [ j ] f[i][j]=f[i+1][j]+f[i][j-1]-f[i+1][j-1]+g[i][j] f[i][j]=f[i+1][j]+f[i][j1]f[i+1][j1]+g[i][j] ,最后处理一下边界。(还是只有错误复杂度的代码)。

#include<bits/stdc++.h>
using namespace std;
struct IO{
    inline char read(){
        static const int IN_LEN=1<<18|1;
        static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
    template <typename _Tp> inline IO & operator >> (_Tp&x){
        static char c11,boo;
        for(c11=read(),boo=0;!isdigit(c11);c11=read()){
            if(c11==-1)return *this;
            boo|=c11=='-';
        }
        for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
        boo&&(x=-x);
        return *this;
    }
    inline void push(const char &c) {
    	putchar(c);
	}
	template <class T>
	inline void write(T x){
		if (x < 0) x = -x, push('-');
		static T sta[35];
		T top = 0;
		do {
			sta[top++] = x % 10, x /= 10;
		} while (x);
		while (top) push(sta[--top] + '0');
	}
	template <class T>
	inline void write(T x, char lastChar){
		write(x),push(lastChar);
	}
}io;

int t,slen;
int p[200005];
char s[100005];
char ss[200005];
pair<int,int> Q;
int anss[5005][5005];

int main(){
	scanf("%s",s+1);
	slen=strlen(s+1);
	ss[0]='!',ss[2*slen+1]='#',ss[2*slen+2]='?';
	for(int i=1;i<=slen;++i){
		ss[2*i-1]='#';
		ss[2*i]=s[i];
	}
	int mx=0,id=0;
	for(int i=1;i<=2*slen+1;++i){
		p[i]=((i>=mx)?0:min(p[2*id-i],mx-i));
		while(ss[i-p[i]]==ss[i+p[i]])
			++p[i];
		if(i+p[i]-1>mx){
			mx=i+p[i]-1;
			id=i;
		}
	}
	io>>t;
	while(t--){
		int ans=0;
		int l,r;
		io>>l>>r;
		if(anss[l][r]!=0){
			printf("%d\n",anss[l][r]);
			continue;
		}
		for(int i=2*l;i<=2*r;++i){
			ans+=(min(p[i],min(i-2*l+2,2*r-i+2))/2);
		}
		anss[l][r]=ans;
		printf("%d\n",ans);
	}
}

[贪心,哈希]T4 基因进化

给出序列 s s s ,可以进行翻转操作,使 s 1 , i s_{1,i} s1,i 翻转,但 i i i 只能递增,且有 m m m 个位置不能翻转。 m ≤ n ≤ 3 × 1 0 5 m\leq n\leq 3\times 10^5 mn3×105 ,多组数据, T ≤ 100 T\leq100 T100


对于前 i i i 个数,所能产生的最小的字典序是多少;无论后面的怎么翻,之前的一定是越小越好;对于相邻两
个能翻的位置 i , j    ( i < j ) i,j \,\,(i<j) i,j(i<j) 。前 j j j 个的最小值要么是前 i i i 个的最小值接上 i i i j j j 这一段;要么是 i i i j j j 的翻转接上前 i i i 个的最小值(同时在 i i i j j j 翻转); 每次翻转前判断哪种方式更优。

用哈希+二分的方式判断优劣程度直接用两个队列,记录头插入和尾插入的数; 维护队列的前缀哈希值和后缀哈希值;用这些一定可以拼出一段的哈希值。

#include<bits/stdc++.h>
using namespace std;
#define MAXN (int)(3e5+5)
#define MAXM (int)(6e5+9)
#define Mod 998244353
#define P (int)(1e9+7)
#define Q (int)(1e9+9)
#define GP 10001
#define GQ 10007
#define ll long long
#define ull unsigned long long
#define chkmax(x,y) x=max(x,y)
#define chkmin(x,y) x=min(x,y)
struct IO{
    inline char read(){
        static const int IN_LEN=1<<18|1;
        static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
    template <typename _Tp> inline IO & operator >> (_Tp&x){
        static char c11,boo;
        for(c11=read(),boo=0;!isdigit(c11);c11=read()){
            if(c11==-1)return *this;
            boo|=c11=='-';
        }
        for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
        boo&&(x=-x);
        return *this;
    }
    inline void push(const char &c) {
    	putchar(c);
	}
	template <class T>
	inline void write(T x){
		if (x < 0) x = -x, push('-');
		static T sta[35];
		T top = 0;
		do {
			sta[top++] = x % 10, x /= 10;
		} while (x);
		while (top) push(sta[--top] + '0');
	}
	template <class T>
	inline void write(T x, char lastChar){
		write(x),push(lastChar);
	}
}io;

struct info{
	int x,y;
};

int mul(int a,int b,int p){
	int ret=1;
	while(b){
		if(b&1)
			ret=ret*b%p;
		b>>=1;
		a=a*a%p;
	}
	return ret;
}
info operator +(info a,info b){
	info ans;
	ans.x=(a.x+b.x>=P)?(a.x+b.x-P):(a.x+b.x);
	ans.y=(a.y+b.y>=Q)?(a.y+b.y-Q):(a.y+b.y);
	return ans;
}
info operator -(info a,info b){
	info ans;
	ans.x=(a.x-b.x>=0)?(a.x-b.x):(a.x-b.x+P);
	ans.y=(a.y-b.y>=0)?(a.y-b.y):(a.y-b.y+Q);
	return ans;
}
info operator *(info a,int b){
	info ans;
	ans.x=1ll*a.x*b%P;
	ans.y=1ll*a.y*b%Q;
	return ans;
}
info operator *(info a,info b){
	info ans;
	ans.x=1ll*a.x*b.x%P;
	ans.y=1ll*a.y*b.y%Q;
	return ans;
}
bool operator ==(info a,info b){
	return a.x==b.x && a.y==b.y;
}

info base,powb[MAXM];
info invb,powi[MAXM],sum[MAXM];
void updata(int &x,int y){
	x+=y;
	if(x>=Mod)
		x-=Mod;
}
bool mark[MAXN];
int n,m,l,r,ans[MAXM];
int a[MAXN],powk[MAXN];
void pushback(int x){
	ans[++r]=x;
	sum[r]=sum[r-1]+powb[r]*x;
}
void pushfront(int x){
	ans[--l]=x;
	sum[l-1]=sum[l]-powb[l]*x;
}
bool cmp(int s,int t,int len){
	int l=0,r=len;
	while(l<r){
		int mid=(l+r+1)>>1;
		if((sum[s+mid-1]-sum[s-1])==(sum[t+mid-1]-sum[t-1])*(powb[s-t]))
			l=mid;
		else
			r=mid-1;
	}
	if(l==len || ans[s+l]<ans[t+l])
		return 1;
	else
		return 0;
}
int main(){
	powb[0]=powi[0]=(info){1,1};
	base=(info){GP,GQ};
	invb=(info){mul(GP,P-2,P),mul(GQ,Q-2,Q)};
	for(int i=1;i<MAXM;++i){
		powb[i]=powb[i-1]*base;
		powi[i]=powi[i-1]*invb;
	}
	powk[0]=1;
	for(int i=1;i<MAXN;++i){
		powk[i]=37ll*powk[i-1]%Mod;
	}
	int T;
	io>>T;
	while(T--){
		io>>n>>m;
		for(int i=1;i<=n;++i){
			io>>a[i];
			mark[i]=0;
		}
		for(int i=1;i<=m;++i){
			int x;
			io>>x;
			mark[x]=1;
		}
		ans[l=r=MAXN]=a[1];
		sum[l-1]=(info){0,0};
		sum[l]=powb[l]*a[1];
		int last=1;
		for(int i=2;i<=n;++i){
			if(mark[i]==0){
				int len=i-last;
				int x=l,length=r-l+1;
				for(int j=last+1;j<=i;++j){
					pushback(a[j]);
					pushfront(a[j]);
				}
				int y=l;
				if(cmp(x,y,length+len)){
					while(len--)
						l++;
				}
				else{
					while(len--)
						r--;
				}
				last=i;
			}
		}
		while(last!=n)
			pushback(a[++last]);
		int final=0;
		for(int i=l;i<=r;++i){
			updata(final,1ll*powk[i-l]*ans[i]%Mod);
		}
		io.write(final,'\n');
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值