6581 Vacation

题意

从右到左分别为0-n辆车,每辆车有长度l,起始位置s和速度v,0坐标在左边,不能超车,单车道,问0号车到达0坐标的最短时间。

分析

最短时间考虑二分时间,然后按这个时间从左边第一辆车开始依次计算最终位置,最后判断0号车的位置即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;
const double eps=1e-12;
int l[N],s[N],v[N];
int n;
bool check(double t){
    //判断时间t car能否通过红绿灯
    //第一辆车全速
    double st=1.0*s[n]-1.0*v[n]*t;
    double ed=st+l[n];
    for(int i=n-1;i>=0;i--){
        double tst=1.0*s[i]-1.0*v[i]*t;
        st=max(ed,tst);
        ed=st+l[i];
    }

    return st<=0;
}
int main(void){
    // freopen("in.txt","r",stdin);
    while(~scanf("%d",&n)){
        for(int i=0;i<=n;i++){
            scanf("%d",&l[i]);
        }
        for(int i=0;i<=n;i++){
            scanf("%d",&s[i]);
        }
        for(int i=0;i<=n;i++){
            scanf("%d",&v[i]);
        }
        double l=0.0,r=1e9;
        double ans;
        while(fabs(l-r)>=eps){
            double mid=(l+r)/2;
            if(check(mid)){
                ans=mid;
                r=mid;
            }else{
                l=mid;
            }
        }
        printf("%.10lf\n",ans);
    }
    return 0;
}

6582 Path

具体见 https://www.cnblogs.com/zxcoder/p/11252499.html

6586 String

题意

给一个字符串,以及每个字符出现次数的限制,找到长度为k的字典序最小的且满足次数限制的子序列。

分析

序列自动机预处理出每个位置后面每个字符第一次出现的位置和个数,然后每次取出满足条件的最小字符,即取了该字符后,剩下的字符串也要满足最小条件。

不满足条件的情况可能是

  • 后面某个字符全选也不能满足下限
  • 后面字符数不能满足下限
  • 该字符已选超过上限
  • 选了该字符后,虽然剩下字符单独都能满足限制,但总数不够

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;
char s[N];
int k;
int l[26],r[26],v[26];
int nxt[N][26],cnt[N][26];
int main(void){
    // freopen("in.txt","r",stdin);
    while(~scanf("%s%d",s+1,&k)){
        for(int i=0;i<26;i++){
            v[i]=0;
            scanf("%d%d",&l[i],&r[i]);
        }
        int n=strlen(s+1);
        for(int i=0;i<26;i++){
            nxt[n][i]=cnt[n][i]=0;
        }
        //序列自动机预处理出每个位置后面每个字符第一次出现的位置和个数
        for(int i=n-1;i>=0;i--){
            for(int j=0;j<26;j++){
                nxt[i][j]=nxt[i+1][j];
                cnt[i][j]=cnt[i+1][j];
            }
            nxt[i][s[i+1]-'a']=i+1;
            cnt[i][s[i+1]-'a']++;
        }
        bool flag=true;
        string ans="";
        int t=0;
        //每次贪心取最小的,判断剩下后缀能否满足要求
        for(int i=0;i<k;i++){
            int j=0;
            //从小到大枚举字符,选择可选的
            for(;j<26;j++){
                if(cnt[t][j]){
                    int g=nxt[t][j];
                    bool ac=true;
                    //尝试选择
                    v[j]++;
                    //记录后面还需要每种字符的总个数
                    int ned=0;
                    for(int q=0;q<26;q++){
                        //选的数量可能会超过l[q]
                        ned+=max(0,l[q]-v[q]);
                        //后面的该字符个数不够下限 || 剩下的可选字符数不够下限  || 该字符已选个数大于上限
                        if(cnt[g][q]<l[q]-v[q] || k-i-1<l[q]-v[q] || v[q]>r[q]){
                            ac=false;
                            break;
                        }
                    }
                    //虽然选了这个字符之后,剩余后缀每个字符数量都能满足限制,但加起来总数不够
                    if(k-i-1<ned){
                        ac=false;
                    }
                    v[j]--;
                    if(ac){
                        ans+=(char)('a'+j);
                        v[j]++;
                        t=g;
                        break;
                    }
                }
            }
            if(j==26){
                flag=false;
                break;
            }
        }
        if(flag){
            printf("%s\n",ans.c_str());
        }else{
            printf("-1\n");
        }
    }
    return 0;
}