bzoj3110 K大数查询

博客围绕找第K大问题展开,指出标记时应把大于mid的标为1,若把大于等于mid标为1可能导致答案出错。还介绍了具体做法,将操作按答案范围分两部分,修改和询问操作分别处理,最后提到线段树统计sum可能爆int及输入负数要注意读优。

传送门
找第 K K K大的时候把大于 m i d mid mid的标记为1.
如果把大于等于 m i d mid mid的标为1,那么有可能第 K K K大就是 m i d mid mid而当前统计的 1 1 1的个数大于 K K K(即有多个数为 m i d mid mid),于是答案会被往上调(为了减少 1 1 1的个数)。
也就是说答案范围变为 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]。本来答案是 m i d mid mid。就出锅了。

比如说:3,4,4,4,5 求1~5中的第三大。(显然是4)
假设当前 m i d mid mid为4,于是数列变成0,1,1,1,1。
由于 4 > 3 4>3 4>3,那么二分的答案要上调,要把答案锁定在 [ m i d + 1 , 5 ] [mid+1,5] [mid+1,5]
于是得到了错误答案。

所以要把大于 m i d mid mid的标为1。

具体做法是:把操作按照答案范围分为两部分。
修改操作就按照修改值与 m i d mid mid大小比较,因为对应修改值只会在对应答案范围内产生贡献。

询问 k k k大就按照当前的操作顺序一步一步操作,然后遇见询问操作就统计区间和。和大了就要把答案上调。和小了就先减去这部分和(比它大的数的个数),然后把答案调小。

然后做完之后要把影响消回来,就是代码中区间减的部分。

50000次给50000个位置加上一个数。于是线段树统计sum爆int
输入有负数,注意读优

#include<bits/stdc++.h>
#define lc (root<<1)
#define rc (root<<1|1)
#define len(x) (T[x].r-T[x].l+1)
#define mid ((T[root].l+T[root].r)>>1)
#define ll long long
using namespace std;
const int maxn=5e4+10;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
int n,m,op,a,b,c,cnt,ans[maxn];
struct que{int op,l,r,id;ll k;}Q[maxn],Q1[maxn],Q2[maxn];
struct node{int l,r;ll add,sum;}T[maxn<<2];
inline void pushup(int root){T[root].sum=T[lc].sum+T[rc].sum;}
inline void pushnow(int root,int val){
    T[root].add+=val;
    T[root].sum+=len(root)*val;
}
inline void pushdown(int root){
    if(T[root].add){
        pushnow(lc,T[root].add);
        pushnow(rc,T[root].add);
        T[root].add=0;
    }
}
inline void Update(int root,int l,int r,int val){
    if(l<=T[root].l&&T[root].r<=r)
        return pushnow(root,val);
    pushdown(root);
    if(l<=mid) Update(lc,l,r,val);
    if(r> mid) Update(rc,l,r,val);
    pushup(root);
}
inline ll Query(int root,int l,int r,ll ret=0){
    if(l<=T[root].l&&T[root].r<=r)
        return T[root].sum;
    pushdown(root);
    if(l<=mid) ret+=Query(lc,l,r);
    if(r> mid) ret+=Query(rc,l,r);
    return ret;
}
inline void build(int root,int l,int r){
    T[root].l=l,T[root].r=r;
    if(l==r) return;
    build(lc,l,mid),build(rc,mid+1,r);
}
inline void Solve(int l,int r,int S,int T){
    if(l>r||S>T) return;
    if(l==r){
        for(int i=S;i<=T;++i) if(Q[i].op==2)
            ans[Q[i].id]=l;
        return;
    }
    int Mid=(l+r)>>1,num1=0,num2=0;
    for(int i=S;i<=T;++i){
        if(Q[i].op==1){
            if(Q[i].k>Mid)
                Update(1,Q[i].l,Q[i].r,1),Q2[++num2]=Q[i];
            else Q1[++num1]=Q[i];
        }
        else{
            ll tmp=Query(1,Q[i].l,Q[i].r);
            if(tmp>=Q[i].k) Q2[++num2]=Q[i];
            else Q[i].k-=tmp,Q1[++num1]=Q[i];
        }
    }
    for(int i=1;i<=num2;++i) if(Q2[i].op==1) Update(1,Q2[i].l,Q2[i].r,-1);
    for(int i=1;i<=num1;++i) Q[S+i-1]=Q1[i];
    for(int i=1;i<=num2;++i) Q[S+num1+i-1]=Q2[i];
    Solve(l,Mid,S,S+num1-1),Solve(Mid+1,r,S+num1,T);
}
int main(){
    n=read(),m=read(),build(1,1,n);
    for(int i=1;i<=m;++i){
        Q[i].op=read(),Q[i].l=read(),Q[i].r=read(),Q[i].k=(ll)read();
        if(Q[i].op==2) Q[i].id=++cnt;
    }
    Solve(1,n,1,m);
    for(int i=1;i<=cnt;++i)
        printf("%d\n",ans[i]);
}
问题陈述 在AtCoder的国家有 $N$ 个动物园,编号为 $1$ 到 $N$ 。 $i$ 动物园门票为 $C_i$ 日元。 铃木先生喜欢 $M$ 种动物,动物 $1,\dots,M$ 。 $i$ 可以在 $K_i$ 动物园看到,即动物园 $A_{i,1},\dots,A_{i,K_i}$ 。 求所有动物每种至少看两次所需的最低总入场费。 如果你多次访问同一个动物园,那里的动物被认为每次访问一次。 # # #约束 —— $1 \le N \le 10$ —— $1 \le M \le 100$ —— $0 \le C_i \le 10^9$ —— $1 \le K_i \le N$ —— $1 \le A_{i,j} \le N$ —— $j \neq j' \Longrightarrow A_{i,j} \neq A_{i,j'}$ —所有输入值均为整数。 # # #输入 输入来自标准输入,格式如下: $N$ $M$ $C_1$ $\dots$ $C_N$ $K_1$ $A_{1,1}$ $\dots$ $A_{1,K_1}$ $\vdots$ $K_M$ $A_{M,1}$ $\dots$ $A_{M,K_M}$ ###输入1 4 3 1000 300 700 200 3 1 3 4 3 1 2 4 2 1 3 ###输出示例 1800 例如,下面的时间表实现了看到 $1,2,3$ 动物至少两次,共计 $1800$ 日元: -去动物园 $3$ 。支付 $700$ 日元,看到动物 $1$ 和 $3$ 。 -去动物园 $3$ 。支付 $700$ 日元,看到 $1$ 和 $3$ 动物。 -去动物园 $4$ 。支付 $200$ 日元,可以看到 $1$ 和 $2$ 动物。 -去动物园 $4$ 。支付 $200$ 日元,看到动物 $1$ 和 $2$ 。 ###示例输入2 7 6 500 500 500 500 500 500 1000 3 1 2 7 3 2 3 7 3 3 4 7 3 4 5 7 3 5 6 7 3 6 1 7 ###示例输出2 2000 通过参观两次动物园 $7$ ,您可以看到 $1,2,3,4,5,6$ 每只动物两次,共计 $2000$ 日元。 写出这道题目的c++代码,并保证通过所有样例,运行时间在2s内,运行内存在256mb内
05-04
【路径规划】(螺旋)基于A星全覆盖路径规划研究(Matlab代码实现)内容概要:本文围绕“基于A星算法的全覆盖路径规划”展开研究,重点介绍了一种结合螺旋搜索策略的A星算法在栅格地图中的路径规划实现方法,并提供了完整的Matlab代码实现。该方法旨在解决移动机器人或无人机在未知或部分已知环境中实现高效、无遗漏的区域全覆盖路径规划问题。文中详细阐述了A星算法的基本原理、启发式函数设计、开放集与关闭集管理机制,并融合螺旋遍历策略以提升初始探索效率,确保覆盖完整性。同时,文档提及该研究属于一系列路径规划技术的一部分,涵盖多种智能优化算法与其他路径规划方法的融合应用。; 适合人群:具备一定Matlab编程基础,从事机器人、自动化、智能控制及相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于服务机器人、农业无人机、扫地机器人等需要完成区域全覆盖任务的设备路径设计;②用于学习和理解A星算法在实际路径规划中的扩展应用,特别是如何结合特定搜索策略(如螺旋)提升算法性能;③作为科研复现与算法对比实验的基础代码参考。; 阅读建议:建议结合Matlab代码逐段理解算法实现细节,重点关注A星算法与螺旋策略的切换逻辑与条件判断,并可通过修改地图环境、障碍物分布等方式进行仿真实验,进一步掌握算法适应性与优化方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值