2018 ICPC 青岛网络赛 Couleur

本文介绍了一种通过启发式分解和利用红黑树数据结构来高效计算序列中逆序对数量的方法。该方法适用于需要频繁更新并查询逆序对的应用场景,如动态数组操作等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给出一个长度为n的序列,每次将某个子序列分成两段,输出所有段中逆序对最大的数目。

题解:

假设从x位置断开,找到x最左边的断点l,和最右边的断点r,那么就是把区间(l,r)分解为(l,x)和(x,r),如何维护两段的逆序对个数呢?

启发式分解

假设断点更靠近r,我们暴力求解出(x,r)的逆序对个数,再求解出(l,x)和(x,r)中两段各有一个数的逆序对的个数,也就是有交叉的逆序对个数,再让(l,r)中的逆序对个数减去那两项就是(l,x)的逆序对个数。

暴力求解的方法:

将区间(x,r)中的元素建立名次树,每加入一个元素之前看树中大于它的个数,累加就是(x,r)的逆序对个数。

(l,x)是一颗名次树,枚举(x,r)中的元素,看在(l,x)中大于它的个数,累加有交叉的逆序对个数。

进而求得(l,x)的逆序对个数。

假设断点更靠近l,方法基本和上面的一样,不过需要一个交换树的操作,原因请自己思考。

用的函数是pb_ds中rb_tree中的order_of_key

注意由于rb_tree中不能有重复元素,需要用结构体,然后重载大于号

#include<bits/stdc++.h>
#include<hash_map>
#define N 100010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define ook order_of_key
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
struct node{
    int v,id;
    node(int a,int b){v=a;id=b;}
    bool operator >(node b) const
    {return v==b.v?id>b.id:v>b.v;}
};
tree<node,null_type,greater<node>,rb_tree_tag,tree_order_statistics_node_update> T[N];
int a[N];
set<int>st; map<int,LL>mp; multiset<LL>A;
int f[N];
int main()
{
    int TT;
    sc(TT);
    while(TT--)
    {
        int n; sc(n);
        for (int i=1;i<=n;i++) sc(a[i]),f[i]=i;f[0]=0;
        LL ans=0; st.clear(); mp.clear(); A.clear();
        st.insert(0); st.insert(n+1);
        for (int i=1;i<=n;i++)
        {
            ans+=T[0].order_of_key(node(a[i],i));
            T[0].insert(node(a[i],i));
        }
        mp[0]=ans; A.insert(ans);
        for (int i=1;i<=n;i++)
        {
            auto p=A.end(); p--; ans=*p;
            printf("%lld",ans); if (i<n) printf(" ");else puts("");
            int x,l,r; sc(x); x^=ans;
            auto pos=st.upper_bound(x); r=*pos;
            pos--; l=*pos; LL tot=mp[l],tt=0,tmp=0;
            A.erase(A.lb(tot)); st.insert(x);
            if (r-x<x-l)
            {
                int tl=f[l],tx=f[x];
                for (int i=x+1;i<r;i++)
                {
                    T[tl].erase(node(a[i],i));
                    tmp+=T[tx].ook(node(a[i],i));
                    T[tx].insert(node(a[i],i));
                }
                mp[x]=tmp; A.insert(tmp);
                for (int i=x+1;i<r;i++)
                    tmp+=T[tl].ook(node(a[i],i));
                tot=tot-tmp-T[tl].ook(node(a[x],x));
                mp[l]=tot; A.insert(tot);
                T[tl].erase(node(a[x],x));
            }else
            {
                swap(f[l],f[x]);
                int tl=f[l],tx=f[x];
                for (int i=l+1;i<x;i++)
                {
                    T[tx].erase(node(a[i],i));
                    tmp+=T[tl].ook(node(a[i],i));
                    T[tl].insert(node(a[i],i));
                }
                mp[l]=tmp; A.insert(tmp);
                for (int i=l+1;i<x;i++)
                    tmp+=r-x-T[tx].ook(node(a[i],i));
                T[tx].erase(node(a[x],x));
                tot=tot-tmp-r+x+1+T[tx].ook(node(a[x],x));
                mp[x]=tot; A.insert(tot);
            }
        }
    }
    return 0;
}

UPDATA

不用结构体,用long long会更快一点

#include<bits/stdc++.h>
#include<hash_map>
#define N 100010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define ook order_of_key
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
tree<LL,null_type,greater<LL>,rb_tree_tag,tree_order_statistics_node_update> T[N];
LL a[N];
set<int>st; unordered_map<int,LL>mp; multiset<LL>A;
int f[N];

int main()
{
    int TT;
    sc(TT);
    while(TT--)
    {
        int n,x; sc(n);
        for (int i=1;i<=n;i++) { sc(x); a[i]=x; a[i]=a[i]<<20|i; f[i]=i;} f[0]=0;
        LL ans=0; st.clear(); mp.clear(); A.clear();
        st.insert(0); st.insert(n+1);
        for (int i=1;i<=n;i++)
        {
            ans+=T[0].order_of_key((a[i]));
            T[0].insert((a[i]));
        }
        mp[0]=ans; A.insert(ans);
        for (int i=1;i<=n;i++)
        {
            auto p=A.end(); p--; ans=*p;
            printf("%lld",ans); if (i<n) printf(" ");else puts("");
            int x,l,r; sc(x); x^=ans;
            auto pos=st.upper_bound(x); r=*pos;
            pos--; l=*pos; LL tot=mp[l],tt=0,tmp=0;
            A.erase(A.lb(tot)); st.insert(x);
            if (r-x<x-l)
            {
                int tl=f[l],tx=f[x];
                for (int i=x+1;i<r;i++)
                {
                    T[tl].erase((a[i]));
                    tmp+=T[tx].ook((a[i]));
                    T[tx].insert((a[i]));
                }
                mp[x]=tmp; A.insert(tmp);
                for (int i=x+1;i<r;i++)
                    tmp+=T[tl].ook((a[i]));
                tot=tot-tmp-T[tl].ook((a[x]));
                mp[l]=tot; A.insert(tot);
                T[tl].erase((a[x]));
            }else
            {
                swap(f[l],f[x]);
                int tl=f[l],tx=f[x];
                for (int i=l+1;i<x;i++)
                {
                    T[tx].erase((a[i]));
                    tmp+=T[tl].ook((a[i]));
                    T[tl].insert((a[i]));
                }
                mp[l]=tmp; A.insert(tmp);
                for (int i=l+1;i<x;i++)
                    tmp+=r-x-T[tx].ook((a[i]));
                T[tx].erase((a[x]));
                tot=tot-tmp-r+x+1+T[tx].ook((a[x]));
                mp[x]=tot; A.insert(tot);
            }
        }
    }
    return 0;
}

 

 

 

### 关于2024 ICPC网络预选的信息 #### 参规则 国际大学生程序设计竞ICPC)作为一项全球性的事,其参规则通常保持一定的稳定性。根据以往的经验和惯例[^1],每支参队伍由三名队员组成,他们需在同一台计算机上合作完成一系列算法问题的解答。比期间不允许使用任何外部资源或工具书,仅能依赖团队成员的知识储备以及现场提供的少量参考资料。 对于具体的2024年ICPC网络预选而言,虽然官方尚未发布详细的最新规定,但可以推测基本框架不会发生显著变化。例如,在线形式可能继续沿用近年来因疫情而普及的方式;同时也会严格监控作弊行为以维护公平竞争环境[^2]。 #### 时间安排 关于2024年的具体时间表目前还没有确切消息公布出来。然而按照传统模式来看,整个流程一般会经历以下几个阶段: - **报名期**:预计会在年初开放注册通道供各高校提交候选名单。 - **区域选拔/网络挑战**:这通常是多轮次举行,覆盖不同大洲和地区的时间段以便更多选手参与进来体验实战氛围并争取晋级机会。 - **全球总决准备阶段**:成功突围进入最终环节者将获得额外培训指导来提升实力迎接巅峰对决时刻。 以下是基于历史数据猜测的一个大致日程示例(请注意实际日期应参照官方通知为准): | 阶段 | 开始日期 | 结束日期 | |--------------------|---------------|----------------| | 报名截止 | 2024-02-15 | | | 初步筛选结果公告 | | 2024-03-01 | | 第一轮网络资格 | 2024-03-10 | 2024-03-12 | | 复活 | 2024-04-07 | 2024-04-09 | | 半决分区 | 各区自行决定 | 各区自行决定 | #### 题目解析 由于正式试题还未出炉之前无法提供针对性分析,不过可以根据往届经典案例来进行一些通用技巧分享: ```python def example_problem(): """ 假设有一道简单的字符串匹配问题. 给定两个长度不超过1e6 的字符串S 和T(S >= T),判断是否存在子串使得两者完全一致. 如果存在返回True;否则False. """ from collections import defaultdict def kmp_preprocess(pattern): lps = [0]*len(pattern) j=0;i=1; while i<len(pattern): if pattern[i]==pattern[j]: lps[i]=j+1;j+=1;i+=1; elif j!=0: j=lps[j-1]; else : lps[i]=0;i+=1 return lps; s="abcde";t="bcd"; lps=kmp_preprocess(t); q=[]; res=False; for char in s: while(q and (not t[len(q)]==char)): last=len(q)-lps[-1];q=q[:last]; if not q or t[len(q)]==char:q.append(char); if ''.join(q)==t:res=True;break; print(res) example_problem() ``` 上述代码片段展示了一个利用KMP算法解决字符串查找的经典方法之一。当然这只是众多可能性中的很小一部分而已。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值