前言
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(bi−i,n−i+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]中质数或两个可重复质数乘积的个数 [l…r]中质数或两个可重复质数乘积的个数
分析
可以预处理 [ 1 … x ] [1\dots x] [1…x]中符合的个数,用前缀和实现询问,然后怎么处理呢,用到线性筛,还是比较水的
代码
#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;
}