2024.08.10美团(有困难题)

1. 小美的密码

小美准备登录美团,需要输入密码,小美忘记了密码,只记得密码可能是 n个字符串中的一个。
小美会按照密码的长度从小到大依次尝试每个字符串,对于相同长度的字符串,小美随机尝试,并且相同的密码只会尝试一次。
小美想知道,她最少需要尝试多少次才能登录成功,最多需要尝试多少次才能登录成功。

哈希找规律
int main() {
    int n;
    cin>>n;
    string target;
    cin>>target;
    map<int,int> m;//记录对应长度字符串个数
    unordered_set<string> st;//避免重复
    for(int i=0;i<n;i++){
        string cur;
        cin>>cur;
        if(st.count(cur)) continue;
        st.insert(cur);
        m[cur.size()]++;
    }
    int res = 0;
    for(auto [key,val]:m){
        if(key==target.size()) break;
        res += val;
    }
    cout<<res+1<<" "<<res+m[target.size()];
    return 0;
}

2. 小美的数组删除

小美有一个长度为 n 的数组 a1,a2,....,an ,他可以对数组进行如下操作:
● 删除第一个元素 a1,同时数组的长度减一,花费为 x。
● 删除整个数组,花费为 k*MEX(a) (其中 MEX(a) 表示 a 中未出现过的最小非负整数。例如 [0,1,2,4] 的 MEX 为 3 )。
小美想知道将 a 数组全部清空的最小代价是多少,请你帮帮他吧。

顺序遍历同时维护快速查询的最小非负整数(使用动态规划+队列+哈希)
int main() {
    int T;
    cin>>T;
    while(T--){
        int n,k,x;
        cin>>n>>k>>x;//元素数量、删除整个数组的花费系数、删除单个元素的花费
        vector<int> nums(n);
        for(int i=0;i<n;i++)
            cin>>nums[i];
        int res= INT_MAX;
        queue<int> q;
        for(int i=0;i<=n;i++)
            q.push(i);
        unordered_set<int> st;//记录已经出现的整数
        vector<int> dp(n);//dp[i]表示以i为开头,之后数组不存在的最小非负整数
        for(int i=n-1;i>=0;i--){
            st.insert(nums[i]);
            while(st.count(q.front()))
                q.pop();
            dp[i] = q.front();
        }
        int delpre = 0;
        for(int i=0;i<n;i++){
            res = min(res,delpre+k*dp[i]);
            delpre+=x;
        }
        cout<<res;
    }
    return 0;
}

3. 小美的彩带

小美的彩带是由一条长度为 n 的彩带一直无限循环得到的,彩带的每一个位置都有一个颜色,用 ai 表示。
因此当 i>n 时,ai = ai-n  。小美每次会从左往后或从右往左剪一段长度为 x 的彩带,她想知道她每次剪下来的彩带有多少种颜色。

树状数组
离线查询,将查询点与插入点一同排序,查询按区间右侧点排
同时需要将将裁剪转化为查询区间
每次查询的时候,插入相同的时候,将上一个位置删除掉,保证[l,r] = sum(r)-sum(l-1),这样只会计算出现过一次的

vector<int> tree;
int lowbit(int x){
    return x&(-x);
}
void updata(int i,int k){
    while(i<=tree.size()){
        tree[i] += k;
        i+=lowbit(i);
    }
}
int getsum(int i){
    int res = 0;
    while(i>0){
        res+=tree[i];
        i-=lowbit(i);
    }
    return res;
}

int main() {
    int n, q;//彩带长度和切带次数
    cin>>n>>q;

    tree.resize(2*n+1);//树状数组
    vector<int> color(n);//记录每个位置的颜色
    for(int i=0;i<n;i++)
        cin>>color[i];
    vector<char> dir(q); 
    vector<int> len(q); 
    for(int i=0;i<q;i++)
        cin>>dir[i]>>len[i];
    //首先要将查询转化为查询区间,将查询区间与插入点(颜色)一同排序
    //按查询区间右侧升序排序,同时插入点在前面
    vector<vector<int>> sorted;
    for(int i=0;i<2*n;i++){//放入插入点(颜色,位置,以及插入点标识)
        sorted.push_back({color[i%n],i+1,-1});//1~2n ,这里循环进行映射,将一段拓展到两段长度
    }

    int l = 1; int r = 2*n;//左端和右端的初始位置
    for(int i=0;i<dir.size();i++){//放入查询区间(左端,右端,查询下标)
        if(dir[i]=='L'){
            while(l>n) l = l-n;//重置一下左端
            int curl = l; int curr = l+len[i]-1; //这里保证如果裁剪长度不超过n,查询时不会出现下标溢出
            sorted.push_back({curl,min(curr,2*n),i});
            l = curr+1;//左端后移一位
        }
        else if(dir[i]=='R'){
            while(r<=n) r = r+n;//重置一下右端
            int curl = r-len[i]+1;   int curr = r;//这里保证如果裁剪长度不超过n,查询时不会出现下标溢出
            sorted.push_back({curl,max(1,curr),i});
            r = curl-1;//右端前移一位
        }
    }
    sort(sorted.begin(),sorted.end(),[&](auto &a,auto &b){
        if(a[1]==b[1]) return a[2]<b[2];//插入点在前,查询点在后
        return a[1]<b[1];
    });

    vector<int> res(q);
    unordered_map<int,int> m;//记录某个颜色上次出现位置
    for(auto &query:sorted){//遍历插入点和查询点
        if(query[2]==-1){//如果是插入点,对树状数组进行更新
            if(m.count(query[0])){//如果该颜色存在,先剔除掉,反正对于后面的查询,该点不重要
                updata(m[query[0]],-1);
            }
            updata(query[1],1);
            m[query[0]] = query[1];//更新位置
        }
        else{//如果是查询点,则进行查询
            int idx = query[2];//第几个查询结果
             res[idx] = getsum(query[1])-getsum(query[0]-1);
        }
    }
    for(auto num:res)
        cout<<num<<endl;
    return 0;
}
posted @ 2024-09-05 17:33  失控D大白兔  阅读(155)  评论(0编辑  收藏  举报