难度:提高(-)
分数: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 ∣hi−hi−1∣≤1 ,若可能输出最大的可能到达高度。保证 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 n≤108,m≤105 。
真就硬贪,已经排好序了,就直接将 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+1−di 和 ∣ h d i − h d i + 1 ∣ |h_{d_i}-h_{d_{i+1}}| ∣hdi−hdi+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 j−i 。 n ≤ 3 × 1 0 6 n\leq 3\times10^6 n≤3×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 max−min>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 ∣s∣≤5000 , T ≤ 1 0 5 T\leq10^5 T≤105 。
首先介绍
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][j−1]−f[i+1][j−1]+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 m≤n≤3×105 ,多组数据, T ≤ 100 T\leq100 T≤100 。
对于前
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');
}
}