【LittleXi】拼多多后端8.11解题报告

极限ak,开题顺序3421成谜

A、旅行计划

题意:

给定n个旅行计划,每个旅行计划完成的时间只能是xi,xi+d,xi+2d,...,并且每天最多只能完成一个旅游,求最小时间

题解:

可以考虑维护当前的天数result

如果result小于当前旅行计划开始的天数,那么result = day_i

否则求最小的b使得day_i + b * d > result

注意坑就是求b的时候,也可以day_i + b * d = result , 此时要b++

// 1
#include<bits/stdc++.h>
using namespace std;
#define ll long long

void solve(){
    ll n;
    cin>>n;
    vector<vector<ll>> data(n,vector<ll>(3));
    for(ll i= 0;i<n;i++){
        cin>>data[i][0]>>data[i][1]>>data[i][2];
    }
    sort(data.begin(),data.end());
    ll result = 1;
    for(ll i =0;i<n;i++){
        vector<ll> current = data[i];
        if(result < current[1]){
            result = current[1];
        }
        else{
            ll day = current[1];
            ll dis = result - day;
            ll b = dis/current[2] + (dis%current[2] > 0);
            if(day + b * current[2]==result) b ++;
            result = day + b * current[2];
        }
    }
    cout<<result<<endl;
}

signed main(){
    cin.tie(0);cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
}


B、多多的作业

题意:

给定n个作业,作业有开始时间和需要的时间,求最小总作业消耗时间,单个作业消耗时间是作业完成时间减去作业开始时间

题解:

可以考虑使用优先队列进行贪心, 每次获取作业的时候,对于前面那个时间片,我们优先让消耗时间小的作业被计算

代码:

// 2
#include<bits/stdc++.h>
using namespace std;
#define ll long long

void solve(){
    ll n;
    cin>>n;
    vector<pair<ll,ll>> vp(n);
    for(ll i =0;i<n;i++) cin>>vp[i].first>>vp[i].second;
    priority_queue<ll,vector<ll>,greater<ll>> pq;
    ll t = 1 , ans = 0;
    pq.push(vp[0].second);
    for(ll i =1;i<n;i++){
        ll d = vp[i].first - vp[i-1].first;
        while(pq.size()&&d){
            auto x = pq.top();pq.pop();
            ll dis = min(x,d);
            ans += dis * (pq.size() + 1);
            x -= dis ; d -= dis;
            if(x) pq.push(x);
        }
        pq.push(vp[i].second);
    }
    while(pq.size()){
        ll x = pq.top();pq.pop();
        ans += x * (pq.size() + 1);
    }
    cout<<ans;
}

signed main(){
    cin.tie(0);cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
}

C、玫瑰和牡丹

题意:

给一个01串,你可以最多选择一个区间进行反转,0->1 , 1->0 ,求最终abs(cnt[0]-cnt[1])的可能数

题解:

我们可以这样考虑,每次延长我们的[l,r]区间一次,cnt[0] - cnt[1] 要么+1,要么-1,所以我们可以求一个区间,使得cnt[0] - cnt[1] 最大或者最小,这个可以使用前缀和解决,那么对于最大的cnt[0] - cnt[1] , 一定可以每次+2到达,所以沿途的数字都可以到达

代码:

// 3
#include<bits/stdc++.h>
using namespace std;
#define ll long long
void solve(){
    int n ;cin>>n;
    vector<int> a(n);
    for(int&x:a) cin>>x;
    for(int&x:a) if(x==0) x = -1;
    int prmi = 0 , prmx = 0;
    int pr = 0;
    int mx = 0 , mi = 0;
    for(int i = 0;i<n;i++){
        pr += a[i];
        mx = max(mx,pr-prmi);
        mi = min(mi,pr-prmx);
        prmx = max(prmx,pr);
        prmi = min(prmi,pr);
    }
    set<int> se;
    int sm = accumulate(a.begin(),a.end(),0);
    for(int i = sm - 2*mx ; i <= sm -2*mi;i+=2){
        se.insert(abs(i));
    }
    cout<<se.size();
}

signed main(){
    cin.tie(0);cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
}


D、哈希表

难度:区域赛铜牌题目,一股ACM味儿

题意:

哈希函数为f = x % n , 给定一个插入完成的哈希表,要求还原插入顺序,如果多个可能,还原字典序最小的

题解:

很容易想到使用拓扑排序,对于从i位置跳到j位置的数字, 将[i,j-1]的所有点连向j,然后进行拓扑排序+贪心就行,时间复杂度n^2 , 不行

可以考虑优化,删除一个数字之后,一定是右边最靠近这个数字可能被选,可以压进优先队列中,查找右边那个数字可以考虑使用树状数组+二分,时间复杂度nlg^2n

代码:

// 4
#include<bits/stdc++.h>
using namespace std;
#define ll long long
//坐标从0开始直接用的树状数组
template <typename T>
class Fenwick
{
private:
    int n;
    vector<T> c;
    int lowbits(int x){
        return (-x) & x;
    }
    int pre_sum(int i){
        int re = 0;
        for (; i > 0; i -= lowbits(i))
            re += c[i];
        return re;
    }
public:
    explicit Fenwick<T>(vector<T> v){
        this->n = v.size();
        this->c = vector<T>(n+1,0);
        for(int i=0;i<n;i++)
            add(i,v[i]);
    };
    void add(int i, int val){
        ++i;
        for (;i<=n; i += lowbits(i))
            c[i] += val;
    }
    int range_sum(int i,int j){
        ++i;++j;
        return pre_sum(j) - pre_sum(i - 1);
    }
};


void solve(){
    int n;cin>>n;
    vector<int> a(n);
    for(int &x:a) cin>>x;
    priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq;
    map<int,int> mp;
    for(int i =0;i<n;i++){
        if(a[i]==-1) a[i] = 0;
        else if(a[i]%n==i){
            pq.push({a[i],i});
            mp[a[i]] = 1;
        }
    }
    for(int i =0 ;i < n;i++) a.push_back(a[i]);

    vector<int> b = a;
    for(int&x:b) if(x) x = 1;
    Fenwick<int> fw(b);

    vector<int> ans;
    while(pq.size()){
        auto p = pq.top();pq.pop();
        int x = p.first , pos = p.second;
        ans.push_back(x);
        fw.add(pos,-1);fw.add(pos+n,-1);
        int l = pos , r = 2*n;
        while(l+1!=r){
            int m = l+r>>1;
            if(fw.range_sum(pos,m)==0) l = m;
            else r = m;
        }
        l++;
        if(l<2*n&&fw.range_sum(pos,l)==1){
            if(mp[a[l]]) continue; 
            int lp = a[l]%n;
            if(lp > l) l+=n;
            if(fw.range_sum(lp, l - 1)==0){
                mp[a[l]] = 1;
                if(l >= n ) l -= n;
                pq.push({a[l],l});
            }
        }
    }
    for(int x:ans) cout<<x<<" ";
}

signed main(){
    cin.tie(0);cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
}

❤关注LittleXi ,谢谢喵, ACM金牌,持续更新更多题解❤

也可以提供大厂算法辅导喵

全部评论
最后一题没想清楚就觉得可以常数个点做前序节点,最后自己把自己卡了,浪费一个多小时,倒数十分钟切暴力 80。😭 现在想想,最后一题只需要针对后面第一个点的话,其实可以用并查集做到总复杂度O(N)。每次删除后向右指向未删的点,配合路径压缩就ok了。 这个思路过于简单,实现起来也特别短,还是太菜了...
1 回复 分享
发布于 08-12 00:44 广东
第二题不需要给vp先排序吗?我记得测试用例并不是按照开始时间从小到大的啊。
1 回复 分享
发布于 08-12 11:37 北京
100 100 100 0最后一题没时间了,75能进面试吗😭
点赞 回复 分享
发布于 08-11 22:02 陕西
希佬😍
点赞 回复 分享
发布于 08-11 22:04 湖南
佬太强了
点赞 回复 分享
发布于 08-11 22:14 浙江
是acm模式吗
点赞 回复 分享
发布于 08-12 12:00 北京

相关推荐

15 52 评论
分享
牛客网
牛客企业服务