Codeforces Round #700 (div1)

传送门

A. Searching Local Minimum (*1700)

题意

交互题
有一个长度为\(n(1\leq n\leq 1e5)\)的排列\(a\),要求询问小于等于\(100\)次,找到一个极小值点,极小值点\(i\)即为满足\(a_i<min\{a_{i-1},a_{i+1} \}\)的点。定义\(a_0=a_{n+1}=+\infty\)

题解

通过二分维护包含极小值点的区间\([l,r]\),令\(a_{l-1}>a_l\)并且\(a_r<a_{r+1}\),初始时\(l=1,r=n\)
case 1: \(a_{mid}<a_{mid+1}\),区间变成\([l,mid]\)
case 2: \(a_{mid}>a_{mid+1}\),区间变成\([mid+1,r]\)
询问次数为\(2\lceil{log_2{n}}\rceil< 34<100\)

#include<bits/stdc++.h>

#define LL long long
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;

int n;

bool check(int mid){
    int x,y;
    printf("? %d\n",mid);
    fflush(stdout);
    scanf("%d",&x);
    printf("? %d\n",mid+1);
    fflush(stdout);
    scanf("%d",&y);
    return x<y;
}

int main(){
    scanf("%d",&n);
    int l=1,r=n;
    while(r>l){
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    printf("! %d\n",r);
    fflush(stdout);
}

B1. Painting the Array I (*1900)

题意

给出一种构造方案,将一个给定的长度为\(n(1\leq n\leq 1e5)\)的数组\(a\)拆分成两个子序列,子序列中连续的相同的数合并成一个,使得两个子序列长度之和最大,计算最大的长度之和

题解

贪心
设两个队列分别为\(s\)\(t\),扫描数组\(a\),设当前扫描到的元素为\(x\),队列\(s\)\(t\)的末尾元素分别为\(y\)\(z\)
case 1:\(x\)\(y\)或者\(z\)的其中一个相同,另外一个不同,则将\(x\)添加到不同的那个数所在队列的末尾。
case 2:\(x\)\(y\)\(z\)都不相同,则将\(x\)添加到待扫描的元素中\(y\)或者\(z\)出现更早的队列中

#include<bits/stdc++.h>

#define LL long long
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;

const int maxn=100010;
int n,a[maxn],pos[maxn],nxt[maxn];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=0;i<=n;i++) pos[i]=n+1;
    for(int i=n;i>=0;i--){
        nxt[i]=pos[a[i]];
        pos[a[i]]=i;
    }
    int j=0,k=0,ans=0;
    for(int i=1;i<=n;i++){
        if(a[i]==a[j]){
            ans+=a[i]!=a[k];
            k=i;
        }
        else if(a[i]==a[k]){
            ans+=a[i]!=a[j];
            j=i;
        }
        else{
            ans++;
            if(nxt[j]<nxt[k]){
                j=i;
            }
            else{
                k=i;
            }
        }
    }
    printf("%d\n",ans);
}

B2. Painting the Array II (*2100)

题意

给出一种构造方案,将一个给定的长度为\(n(1\leq n\leq 1e5)\)的数组\(a\)拆分成两个子序列,子序列中连续的相同的数合并成一个,使得两个子序列长度之和最小,计算最小的长度之和

题解

方法一:贪心
\(B1\)的构造相反
设两个队列分别为\(s\)\(t\),扫描数组\(a\),设当前扫描到的元素为\(x\),队列\(s\)\(t\)的末尾元素分别为\(y\)\(z\)
case 1:\(x\)\(y\)或者\(z\)的其中一个相同,则将\(x\)添加到相同的那个数所在队列的末尾。
case 2:\(x\)\(y\)\(z\)都不相同,则将\(x\)添加到待扫描的元素中\(y\)或者\(z\)出现更晚的队列中

#include<bits/stdc++.h>

#define LL long long
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;

const int maxn=100010;
int n,a[maxn],pos[maxn],nxt[maxn];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=0;i<=n;i++) pos[i]=n+1;
    for(int i=n;i>=0;i--){
        nxt[i]=pos[a[i]];
        pos[a[i]]=i;
    }
    int j=0,k=0,ans=0;
    for(int i=1;i<=n;i++){
        if(a[i]==a[j]){
            j=i;
        }
        else if(a[i]==a[k]){
            k=i;
        }
        else{
            ans++;
            if(nxt[j]>nxt[k]){
                j=i;
            }
            else{
                k=i;
            }
        }
    }
    printf("%d\n",ans);
}

方法二:dp
扫描数组\(a\),对于\(1\leq j<i\),设\(a[j-1]\)\(a[i]\)在同一个队列中相邻,则将下标为\([j,i-1]\)的元素移动到另一个队列中,考虑一个\(O(n^2)\)的dp:
\(f[i]=min_{1\leq j<i}\{ f[j]+(i-j-1)+[a[j-1]\neq a[i]]\}\)
优化成\(O(n)\)
\(g(i,j)=f(j)+(i-j-1)+[a[j-1]\neq a[i]]\)
转移有两种情况:
1.从\((i-1)\)处转移,\(f[i]=min(f[i],g(i,i-1))\)
2.设之前出现过\(a[i]\)的最近位置为\(last[i]\),则从\(last[i]+1\)处转移,\(f[i]=min(f[i],g(i,last[i]+1))\)

#include<bits/stdc++.h>
#define LL long long
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;
 
const int maxn=100010,inf=1e9+10;
int n,m,a[maxn],f[maxn],pos[maxn],last[maxn];

int g(int i,int j){
    return f[j]+(i-j-1)+(a[j-1]!=a[i]);
}
 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        if(a[i]!=a[i-1]) a[++m]=a[i];
    }
    for(int i=1;i<=m;i++){
        last[i]=pos[a[i]];
        pos[a[i]]=i;
    }
    f[1]=1;
    for(int i=2;i<=m;i++){
        f[i]=g(i,i-1);
        if(last[i]){
            int j=last[i]+1;
            f[i]=min(f[i],g(i,j));
        }
    }
    printf("%d\n",f[m]);
}

C

D

E

posted @ 2021-02-09 15:44  fxq1304  阅读(45)  评论(0编辑  收藏  举报