2018_10_20 模拟赛

前言

OTL × ∞ \times ∞ ×


JZOJ 5456 奇怪的队列

题目

n n n个人排队,身高为 a i a_i ai的人要么左边由 b i b_i bi个比她高的人,要么右边由 b i b_i bi比她高的人,问身高字典序最小的情况


分析

然而朴素就是 n ! × n 2 n!\times n^2 n!×n2,不多讲了,然后直接讲正解,既然要求字典序最小,那么把身高从小到大排序,一个个尽量靠左,那么找从左数可用第 m i n ( b i − i , n − i + b i ) + 1 min(b_i-i,n-i+b_i)+1 min(bii,ni+bi)+1,找不到自然 i m p o s s i b l e impossible impossible,然后修改就可以了,用线段树维护,时间复杂度 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)


代码

#include <cstdio>
#include <algorithm>
#define rr register
using namespace std;
struct rec{int a,b;}re[200001];
int w[262145],bas=1,n,ans[100001];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed getf(int k,int l,int r,int x){
	if (l==r) return l;
	rr int mid=(l+r)>>1;
	if (x<=w[k<<1]) return getf(k<<1,l,mid,x);
	    else return getf(k<<1|1,mid+1,r,x-w[k<<1]);
}
bool cmp(rec x,rec y){return x.a<y.a;}
signed main(){
	freopen("queue.in","r",stdin);
	freopen("queue.out","w",stdout);
	n=iut(); while ((bas<<=1)<n);//为zkw做准备
	for (rr int i=1;i<=n;++i) re[i].a=iut(),re[i].b=iut();
	sort(re+1,re+1+n,cmp); rr int flag=0;//从小到大排序
	for (rr int i=0;i<n;++i) w[bas+i]=1;
	for (rr int i=bas-1;i;--i) w[i]=w[i<<1]+w[i<<1|1];//w[1]表示可用总个数
	for (rr int i=1;i<=n;++i){
		rr int pos,j,now=min(re[i].b,n-i-re[i].b)+1;//应该插入从左数第now个位置
		if (now<=0||now>w[1]){flag=1;break;}//不可能
		ans[pos=getf(1,1,bas,now)]=re[i].a;//找到的点记录身高
		for (w[j=bas+pos-1]=0,j>>=1;j;j>>=1)
		    w[j]=w[j<<1]+w[j<<1|1];//修改标记
	}
	if (flag) return !printf("impossible");//不可能
	for (rr int i=1;i<=n;++i) print(ans[i]),putchar(i==n?'\n':' ');
	return 0;
}

JZOJ 5458 质数

题目

问区间 [ l … r ] 中 质 数 或 两 个 可 重 复 质 数 乘 积 的 个 数 [l\dots r]中质数或两个可重复质数乘积的个数 [lr]


分析

可以预处理 [ 1 … x ] [1\dots x] [1x]中符合的个数,用前缀和实现询问,然后怎么处理呢,用到线性筛,还是比较水的


代码

#include <cstdio>
#define rr register
using namespace std;
const int N=10000000;
int v[N+1],prime[N+1],cnt;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
signed main(){
	freopen("prime.in","r",stdin);
	freopen("prime.out","w",stdout);
	for (rr int i=2;i<=N;++i){
		if (!v[i]) v[i]=1,prime[++cnt]=i;//质数
		for (rr int j=1;j<=cnt&&prime[j]*i<=N;++j){
		    if (v[i]>1) v[i*prime[j]]=prime[j];//i是合数
		        else v[i*prime[j]]=4;//分开标记
		    if (i%prime[j]==0) break;//只经过一次
	    }
	}
	for (rr int i=2;i<=N;++i) v[i]=v[i-1]+(v[i]==1||v[i]==4);//前缀和
	rr int t=iut();
	while (t--){
		rr int l=iut(),r=iut();
		if (v[r]-v[l-1]) print(v[r]-v[l-1]);
		else putchar(48); putchar(10);
	}
	return 0;
}

JZOJ 5462 好文章

题目

求字符串中有多少个不相同的长度为 m m m的子串


分析

sto 后缀自动机的dalao
当然讲的是哈希,因为我菜
这东西还是比较难讲的,具体看我代码
ssl_zyc one of the dalao in SSL的题解
ssl_wyc one of the dalao in SSL的题解


代码

#include <cstdio>
#define rr register
using namespace std;
typedef unsigned uit;
const uit mod=313003;
char s[200001]; uit n,m,hash[mod],ans=1,sum,f=1;
inline signed check(int t1,int t2){//判断是否相同
	for (rr int i=t1;i>t1-m;--i)
	if (s[i]!=s[t2-t1+i]) return 0;
	return 1; 
}
inline signed locate(uit x,uit now){//哈希
	rr uit pos=x%mod,i=0;
	while (hash[(pos+i)%mod]&&!check(hash[(pos+i)%mod],now)) ++i;
	return (pos+i)%mod;
}
signed main(){
	freopen("article.in","r",stdin);
	freopen("article.out","w",stdout);
	scanf("%u%u",&n,&m);
	for (rr uit i=1;i<=n;++i){
		char c=getchar();
		while (c<97||c>122) c=getchar();
		s[i]=c;
	}
	sum=s[1];
	for (rr uit i=2;i<=m;++i)
	    sum=sum*378551+s[i],f*=378551;
	hash[sum%mod]=m;
	for (rr uit i=m+1,g;i<=n;++i){
		sum=(sum-f*s[i-m])*378551+s[i];//构造hash值
		if (!hash[g=locate(sum,i)]) ++ans,hash[g]=i;
	}
	printf("%u",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值