2025 National Invitational of CCPC (Fujian)——F 2025福建邀请赛(福建省赛)

补题链接
在这里插入图片描述

总得来说,思路就是先找到, L [ i ] L[i] L[i] R [ i ] R[i] R[i] ,左右第一个满足偏序条件的索引,即 L [ i ] = j , s . t . j < i & &   x [ j ] > = x [ i ]   & &   y [ j ] > = y [ i ] L[i] = j,s.t. j<i \quad \&\&\ x[j]>=x[i]\ \&\&\ y[j]>=y[i] L[i]=j,s.t.j<i&& x[j]>=x[i] && y[j]>=y[i] , R [ i ] R[i] R[i] 同理。找到之后,考虑每个位置 i i i 能为区间 [ l , r ] [l,r] [l,r] 产生的贡献,我们发现把询问 [ l , r ] [l,r] [l,r] 看成二维平面的点,那么 对于 i i i ,产生的贡献的区域就是 [ L [ i ] + 1 , i ] × [ i , R [ i ] − 1 ] [L[i]+1,i]\times [i,R[i]-1] [L[i]+1,i]×[i,R[i]1] ,贡献为1。

所以,问题被分成两步:
1) 找到L[i],R[i]
2) 对二维区域进行加一操作,询问二维平面上1个点的值

对于第一部分,我们可以线段树二分查询,维护区间最大值。按 x [ i ] , y [ i ] , i x[i],y[i],i x[i]y[i],i 为关键字降序排序,对于每个 x [ i ] x[i] x[i] 比它大的 x x x 已经被处理了,线段树里面放入的是 y y y 的值
坑点: 数据范围从0开始,简单的处理办法是一开始就把所有数值加1
对于第二部分,我们可以采取扫描线的方式,把询问离线下来,处理完询问需要的区域之后再进行算答案,扫描线的方式很多,可以写树状数组维护差分,那么只要实现单点修改,单点查询即可或者线段树实现区间修改,单点查询

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;
constexpr int maxn = 1e6+10;
constexpr int inf = 1e7;

struct Event {
    i64 x;
    int y1, y2;
    int type; // +1 for entering, -1 for leaving
    bool operator<(const Event &p) const {
        return x < p.x;
    }
};

struct Scanline {
    int n;               // number of unique y-coordinates
    vector<int> cnt;     // cover count
    vector<i64> len;      // covered length

    void init(){
        n = 1000010;
        len.assign(n<<2,0);
        cnt.assign(n<<2,0);
    }

    // 向上维护节点覆盖长度
    void pushup(int p, int l, int r) {
        if (cnt[p] > 0) {
            len[p] = r + 1 - l;
        } else if (l == r) {
            len[p] = 0;
        } else {
            len[p] = len[p << 1] + len[p << 1 | 1];
        }
    }

    // 区间更新 [ql, qr]
    void Update(int p, int l, int r, int ql, int qr, int val) { //ql,qr代表要更新的区间
        if (ql > r || qr < l) return;
        if (ql <= l && r <= qr) {
            cnt[p] += val;
            pushup(p, l, r);
            return;
        }
        int mid = (l + r) >> 1;
        Update(p << 1, l, mid, ql, qr, val);
        Update(p << 1 | 1, mid + 1, r, ql, qr, val);
        pushup(p, l, r);
    }

    int query(int p,int l,int r,int x){
        if(l==r) return cnt[p];
        int mid = (l+r)>>1;
        int res = cnt[p];
        if(x <= mid) return res + query(p<<1,    l,   mid, x);
        else         return res + query(p<<1|1, mid+1, r,   x);
    }


    vector<int> calc(vector<Event> &events,vector<array<int,3>> &qr) {
        sort(events.begin(), events.end());
        vector<int> ans(qr.size());
        int now = 0;
        for (int i = 0; i < (int)events.size(); i++) {
            while(now<qr.size()&&qr[now][1]<events[i].x){
                ans[qr[now][2]] = query(1,0,n-2,qr[now][0]);
                ++now;
            }
            Update(1,0,n-2,events[i].y1,events[i].y2,events[i].type);
        }
        while(now<qr.size()){
            ans[qr[now][2]] = query(1,0,n-2,qr[now][0]);
            ++now;
        }
        return ans;
    }
}sc;



int n,q,L[maxn],R[maxn];
array<int,3> a[maxn],b[maxn];
int tree[maxn<<2];
void build(int p,int l,int r){
    tree[p]=-1;
    if(l==r){
        tree[p]=0;
        return;
    }
    int mid =(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
}
void update(int p,int l,int r,int x,int num){
    if(x==l&&l==r){
        tree[p]=num;
        return;
    }
    int mid=(l+r)/2;
    if(x<=mid) update(p*2,l,mid,x,num);
    else update(p*2+1,mid+1,r,x,num);
    tree[p]=max(tree[p*2],tree[p*2+1]);
}

int query_left(int p,int l,int r,int x,int y,int num){//左侧最大的应该先找右侧找不到再找左侧
    if(x>r||y<l) return -1;
    if(x<=l&&r<=y){
        if(tree[p]<num) return -1;
        if(l==r) return l;
    }
    int mid = (l+r)>>1;
    int res = query_left(p<<1|1,mid+1,r,x,y,num);
    if(res!=-1) return res;
    return query_left(p<<1,l,mid,x,y,num);
}

int query_right(int p,int l,int r,int x,int y,int num){
    if(x>r||y<l) return -1;
    if(x<=l&&r<=y){
        if(tree[p]<num) return -1;
        if(l==r) return l;
    }
    int mid = (l+r)>>1;
    int res = query_right(p<<1,l,mid,x,y,num);
    if(res!=-1) return res;
    return query_right(p<<1|1,mid+1,r,x,y,num);
}


void solve(){
    cin>>n>>q;
    for(int i =1 ;i<=n;++i) cin>>a[i][0];
    for(int i = 1;i<=n;++i){
        cin>>a[i][1];
        a[i][2] = -i;
        b[i]=a[i];
    }
    a[0]=a[n+1]={inf,inf,-1};
    sort(b+1,b+n+1,greater<array<int,3>> ());
    memset(tree,-1,sizeof tree);
    update(1,1,n+2,1,inf);//0->1
    update(1,1,n+2,n+2,inf);

    for(int i = 1,j=1;i<=n;++i){
        while(j<=n&&b[j][0]>=b[i][0]){
            int idx = -b[j][2]+1;
            update(1,1,n+2,idx,b[j][1]);
            ++j;
        }
        L[-b[i][2]] = query_left(1,1,n+2,1,-b[i][2],b[i][1])-1;
    }

    memset(tree,-1,sizeof tree);
    update(1,1,n+2,1,inf);//0->1
    update(1,1,n+2,n+2,inf);
    
    for(int i =1,j=1;i<=n;++i){
        while(j<=n&&b[j][0]>=b[i][0]){
            int idx = -b[j][2]+1;
            update(1,1,n+2,idx,b[j][1]);
            ++j;
        }
        R[-b[i][2]] = query_right(1,1,n+2,-b[i][2]+2,n+2,b[i][1])-1;
    }

    vector<array<int,3>> qr(q);
    for(int i = 0;i<q;++i){
        cin>>qr[i][0]>>qr[i][1];
        qr[i][2] = i;
    }
    sort(qr.begin(),qr.end(),[&](auto &A,auto & B){
        return A[1]<B[1];
    });

    //扫描线部分
    vector<Event> events;
    for(int i = 1;i<=n;++i){//[L[i]+1,i] 叉乘 [i,R[i]-1]
        events.emplace_back(i,L[i]+1,i,1);
        events.emplace_back(R[i],L[i]+1,i,-1);
    }
    sc.init();
    vector<int> ans = sc.calc(events,qr);
    for(auto &i:ans) cout<<i<<"\n";

}

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int t=1;
    //cin>>t;
    while(t--) solve();
    return 0;
}
/*
10 1
1 0 7 9 3 0 4 7 5 9
7 7 2 0 4 4 0 5 9 7
*/
目前尚未找到关于2024年河北省CCPC的具体官方信息。然而,可以基于以往的比情况推测一些可能的内容。 ### 参准备与注意事项 通常情况下,CCPC(中国大学生程序设计竞)会在每年的秋季举行区域。参者需注意以下几点: 1. **团队协作** 每支队伍由三名队员组成,分工合作至关重要。例如,在之前的比中,有队伍因签到题失误而导致时间浪费和罚时增加[^1]。因此,合理分配题目并快速定位错误是成功的关键。 2. **算法模板储备** 动态规划(DP)、图论、字符串处理等常见算法应熟练掌握。比如动态规划的一个经典实现如下所示: ```cpp #include <bits/stdc++.h> using namespace std; int k, n, m, dp[1005][1005]; int a[1005], b[1005]; int pa[1005] = {0}, pb[1005] = {0}; queue<int> q[1005]; int main() { int ak; cin >> ak; while (ak--) { for (int i = 1; i <= n; i++) while (!q[i].empty()) q[i].pop(); scanf("%d%d%d", &k, &n, &m); memset(dp, 0, sizeof(dp)); memset(pa, 0, sizeof(pa)); memset(pb, 0, sizeof(pb)); for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= m; i++) cin >> b[i]; for (int i = 1; i <= n; i++) { q[a[i]].push(i); if (q[a[i]].size() == k) { pa[i] = q[a[i]].front(); q[a[i]].pop(); } } for (int i = 1; i <= n; i++) while (!q[i].empty()) q[i].pop(); for (int i = 1; i <= m; i++) { q[b[i]].push(i); if (q[b[i]].size() == k) { pb[i] = q[b[i]].front(); q[b[i]].pop(); } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); if (a[i] == b[j] && pa[i] != 0 && pb[j] != 0) dp[i][j] = dp[pa[i] - 1][pb[j] - 1] + k; } } printf("%d\n", dp[n][m]); } return 0; } ``` 3. **模拟训练** 定期参加ACM/ICPC风格的在线比或学校内部模拟有助于提升实战能力。通过解决类似矩形面积计算等问题来增强几何算法的理解[^3]。 ### 事安排预测 虽然具体日期未公布,但根据往年惯例,建议关注以下几个方面: - 报名截止时间和方式; - 初步计划中的比地点及日程表; - 是否存在线上预选环节。 #### 提醒事项 务必留意官网公告以及教练通知,及时调整复习策略以应对可能出现的新变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值