The 18th Northeast Collegiate Programming Contest G

第18届东北高校编程竞赛G题补题代码分享

补题链接
看网上好像不怎么找不到代码,发一份出来

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;

constexpr int maxn = 1e5+10,k = 316;
int n,m,a[maxn],f[maxn];
vector<int> pos[maxn];
i64 pre[320][maxn];

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n>>m;
    for(int i = 1;i<=n;++i){
        cin>>a[i];
        pos[a[i]].emplace_back(i);
    }
    for(int i = 1;i<=n;++i){
        if(pos[i].size()>k){
            f[0]++;
            f[i] = f[0];
            for(int j = 1;j<=n;++j){
                pre[f[i]][j] = pre[f[i]][j-1]+(a[j]==i);
            }
            for(int j = 1;j<=n;++j){
                int lst_p =0;
                for(int p:pos[j]){
                    pre[f[i]][p] = pre[f[i]][p]+pre[f[i]][lst_p];//真正有用位置的前缀和,二次累加
                    lst_p = p;
                }
            }
        }
    }
    for(int i = 1;i<=n;++i) pos[i].emplace_back(n+10);
    for(int qry=1;qry<=m;++qry){
        int l,r,p,q;//题目保证p<q
        cin>>l>>r>>p>>q;
        if(pos[p].size()<=k+1&&pos[q].size()<=k+1){
            int pl=0,np=0,nq=0,nn=0;//np,nq记录p,q的指针指到哪里了
            i64 ans =0;
            while(np<pos[p].size()||nq<pos[q].size()){
                if(np==pos[p].size()){
                    pl = pos[q][nq];
                    nq++;
                }else if(nq==pos[q].size()){
                    pl = pos[p][np];
                    np++;
                }else if(pos[q][nq]<pos[p][np]){
                    pl = pos[q][nq];
                    ++nq;
                }else{
                    pl = pos[p][np];
                    np++;
                }
                if(l<=pl&&pl<=r){
                    if(a[pl]==q) ++nn;
                    else ans+=nn;//累加每个q的贡献
                }
            }
            cout<<ans<<"\n";
        }else{
            if(pos[p].size()<pos[q].size()) swap(p,q);//可能导致求成顺序对
            i64 ans =0 ;
            int pl = lower_bound(pos[p].begin(),pos[p].end(),l)-pos[p].begin();
            int pr = upper_bound(pos[p].begin(),pos[p].end(),r)-pos[p].begin();
            pr--;pl--;//相当于[l-1,r]

            int ql = lower_bound(pos[q].begin(),pos[q].end(),l)-pos[q].begin();
            int qr = upper_bound(pos[q].begin(),pos[q].end(),r)-pos[q].begin();
            qr--;ql--;

            i64 nn = pr-pl;
            i64 nnq = qr-ql;
            if(nn>0&&nnq>0){
                if(qr>=0) qr = pos[q][qr];
                else qr =0;
                if(ql>=0) ql = pos[q][ql];
                else ql=0;

                if(ql<=qr) ans+=(pre[f[p]][qr]-pre[f[p]][ql])-nnq*(pl+1);
                if(p<q) ans = nn*nnq-ans;
            }
            cout<<ans<<"\n";
        }
    }
    return 0;
}

### 计算最大可发送邮件数量的算法设计 在计算给定时间表中最多可以发送的电子邮件数量时,可以将其视为一个调度问题。该问题的核心是优化资源分配(如带宽或服务器容量),以最大化单位时间内完成的任务数。以下是一个基于贪心算法的设计方案[^4]。 #### 1. 问题建模 假设每封邮件的发送需要一定的时间窗口 `[start, end]`,并且在同一时刻,系统只能处理有限数量的邮件(例如 `k`)。目标是在满足约束条件的情况下,尽可能多地发送邮件。 - 输入:一组邮件的时间窗口 `{[s1, e1], [s2, e2], ..., [sn, en]}` 和并发限制 `k`。 - 输出:最大可发送邮件数量。 #### 2. 算法步骤 以下是解决该问题的一个贪心算法实现: ```python def max_emails_sent(emails, k): # 按结束时间排序 emails.sort(key=lambda x: x[1]) # 排序依据结束时间 [^5] count = 0 active = [] # 当前活跃的邮件任务 for email in emails: start, end = email # 移除已经结束的任务 while active and active[0] <= start: active.pop(0) # 如果最早结束的任务已经完成,则移除 # 检查当前活跃任务是否超过限制 if len(active) < k: active.append(end) # 添加当前任务到活跃列表 count += 1 # 增加发送计数 active.sort() # 维护活跃任务的顺序 return count ``` #### 3. 算法解释 - **排序**:首先按照邮件的结束时间对所有任务进行排序。这是贪心策略的关键,优先选择结束时间较早的任务,以便为后续任务腾出更多空间。 - **维护活跃任务**:使用一个列表 `active` 来记录当前正在处理的任务。每当遇到一个新的任务时,检查是否有任务已经完成(即其结束时间小于等于当前任务的开始时间)。 - **限制检查**:确保同时处理的任务数不超过 `k`。如果当前活跃任务数小于 `k`,则将新任务加入活跃列表,并增加计数器。 #### 4. 时间复杂度分析 - 排序的时间复杂度为 `O(n log n)`。 - 遍历任务列表的时间复杂度为 `O(n)`,其中每次操作涉及对 `active` 列表的插入和删除,最坏情况下为 `O(k)`。 - 因此,总时间复杂度为 `O(n log n + nk)`。 #### 5. 示例 假设输入如下: ```python emails = [[1, 3], [2, 5], [6, 8], [4, 7], [9, 10]] k = 2 ``` 调用 `max_emails_sent(emails, k)` 的结果为 `4`,即最多可以发送 4 封邮件。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值