8.22NOIP Day8模拟赛
T1
显然的,我们可以维护 \(s\) 中每一个位置下一个 \(a\sim z\) 出现的位置,如果没有就从头开始并把答案加 \(1\),然后对于 \(t\) 从前到后跑一遍记录当前位置即可
#include<bits/stdc++.h>
#define N 500005
using namespace std;
int nxt[N][30];
char s[N],t[N];
signed main(){
scanf("%s%s",s+1,t+1);
int n=strlen(s+1),m=strlen(t+1);
for(int i=n-1;i>=0;i--){
for(int j=0;j<26;j++)nxt[i][j]=nxt[i+1][j];
nxt[i][s[i+1]-'a']=i+1;
}
for(int i=1;i<=n;i++)for(int j=0;j<26;j++)if(!nxt[i][j])nxt[i][j]=nxt[0][j];
int now=0,ans=1;
for(int i=1;i<=m;i++){
if(!nxt[now][t[i]-'a']){
printf("-1\n");
return 0;
}
if(nxt[now][t[i]-'a']<=now)ans++;
now=nxt[now][t[i]-'a'];
}
printf("%d\n",ans);
return 0;
}
T2
对于一个合法的 \((l,r)\),它需要满足 \((l,r-1)\) 或 \((l+1,r)\) 合法,否则就无法取走一个然后依旧保持平衡
故对于每一个 \(1\le i\le n\),都存在一个长度为 \(i\) 的区间的重心在箱子上是合法解的必要性条件,由于如果 \((l,r)\) 以及另外一个长度为 \(r-l\) 的区间重心在箱子上,那么 \((l,r)\) 的子区间 \((l+1,r),(l,r-1)\) 中的一个重心一定在箱子上,所以这是充要条件。
设 \((1,n)\) 的重心为 \(u\),那么可以判断出对于每一个长度 \(i\),选择重心最靠近 \(u\) 的两个之一(左右两个)一定是最优的,所以考虑二分找到这两个区间存下来,然后按照重心位置排序,跑一个双指针求解最小值
#include<bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
int x[N],y[N],cnt[N];
struct Ty{
double u;
int id;
bool operator <(const Ty &a){return u<a.u;}
}Q[N*2];
signed main(){
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&x[i]);
for(int i=1;i<=n;i++)y[i]=y[i-1]+x[i];
double u=y[n]/1.0/n;
for(int i=1;i<n;i++){
int l=1,r=n-i+1;
while(l<r){
int mid=(l+r+1)/2;
double v=(y[mid+i-1]-y[mid-1])/1.0/i;
if(v<=u)l=mid;
else r=mid-1;
}
Q[i*2-1].u=(y[l+i-1]-y[l-1])/1.0/i;
Q[i*2-1].id=i;
Q[i*2].u=(y[l+i]-y[l])/1.0/i;
Q[i*2].id=i;
}
Q[n*2-1].u=u;
Q[n*2-1].id=n;
sort(Q+1,Q+n*2);
int now=0,summ=0;
double ans=1e9;
for(int i=1;i<n*2;i++){
if(Q[i].u>u)break;
if(i-1){
cnt[Q[i-1].id]--;
if(!cnt[Q[i-1].id])summ--;
}
while(summ<n){
now++;
if(!cnt[Q[now].id])summ++;
cnt[Q[now].id]++;
}
ans=min(ans,Q[now].u-Q[i].u);
}
printf("%.11f",ans);
return 0;
}
T3
观察到我们可以将 \(1\sim n\) 从大到小像冒泡排序一样依次拉到最右边,不同的是可以观察到大致需要 \(\log_{n}\) 次操作就可以移到最右边,总操作数是 \(O(n\log{n})\) 级的
出题入搬题时卡了操作数的常数,我们需要改成常数更小的写法
为了防止恶意构造数据我们需要随机 \(100\) 次操作打乱序列,我也不知道为啥打乱完就均摊 \(O(n)\) 次操作了
还有常数更小的做法,如果说题目给定要求求出把给定的序列整理好的方案,那么我们可以把操作取反一下,然后去求把有序的数列打乱成给定数列的方案,这样操作常数更小
#include<bits/stdc++.h>
#define N 3005
using namespace std;
vector<pair<int,int> >q;
int x[N],y[N],ans=0;
void solve(int l,int r){
q.push_back(make_pair(l,r));
int now=l;
for(int i=l;i<=r;i++)y[i]=x[i];
for(int i=l+1;i<=r;i+=2)x[i]=y[now++];
for(int i=l;i<=r;i+=2)x[i]=y[now++];
return;
}
signed main(){
srand(time(0));
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int u;
scanf("%d",&u);
x[u]=i;
}
for(int i=1;i<=100;i++){
int a=rand()%n+1,b=rand()%n+1;
solve(min(a,b),max(a,b));
}
for(int i=n;i>=1;i--){
int now=0;
for(int j=1;j<=i;j++)if(x[j]==i){
now=j;
break;
}
while(now*2<=i)solve(1,now*=2);
if(now!=i)solve(now*2-i+1,i);
}
reverse(q.begin(),q.end());
int ans=q.size();
printf("%d\n",ans);
for(int i=0;i<q.size();i++)printf("%d %d\n",q[i].first,q[i].second);
return 0;
}

浙公网安备 33010602011771号