zoukankan      html  css  js  c++  java
  • [ZROI 9.15模拟赛] Tutorial

    Link:

    传送门

    可能要补一补之前的题了

    题目名字天(Sky)的(De)炭(C)好评啊……

    A:

    从买/卖物品的配对来考虑:

    可以发现如果当前物品为卖,肯定从之前选最小的(无论其为买/卖),因为贡献都是差值!

    如果要买的物品当前状态为卖,那么相当于将那条匹配链的卖的那一端转换

    用优先队列维护$pair(w[i],0/1)$,0/1分别表示当前为卖/买的状态即可

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=1e5+10;
    int T,n,cnt,dat[MAXN];ll res=0;
    priority_queue<P,vector<P>,greater<P> > q;
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;i++) scanf("%d",&dat[i]);
            res=0;cnt=0;
            while(!q.empty()) q.pop();
            
            q.push(P(dat[1],1));
            for(int i=2;i<=n;i++)
            {
                P t=q.top();
                if(dat[i]>t.X)
                {
                    res+=dat[i]-t.X;
                    cnt+=t.Y;q.pop();
                    if(!t.Y) q.push(P(t.X,1));
                    q.push(P(dat[i],0));
                }
                else q.push(P(dat[i],1));
            }
            printf("%lld %d
    ",res,cnt*2);
        }
        return 0;
    }
    Problem A

    考场上写的$O(n^2)$ $dp$发现并不能优化……

    其实已经想到用匹配来做,但没发现那条可反悔贪心的性质

    一般需要优化的匹配问题除了网络流,都是顺序考虑每一个数而非整体考虑

    这里还要注意必须将卖设为0,因为在$w[i]$相同时要先将卖转移

    (如果优先队列中放的是结构体,注意每个维度的顺序!)

    B:

    关键要将上下边界也看成大的障碍点

    考虑长度$d$不能通过的条件:

    将所有长度小于$d$的连边后上下边界连通,这样一定无法通过这道屏障

    用类似$Kruskal$的方法将所有边排序后用并查集维护连通性即可

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=2e5+10;
    P dat[MAXN];
    int n,l,f[MAXN],tot;
    struct edge{int x,y;double w;}e[MAXN];
    
    double sqr(double a){return a*a;}
    bool cmp(edge a,edge b){return a.w<b.w;}
    void add_edge(int x,int y,double w)
    {e[++tot]=(edge){x,y,w};}
    int find(int x)
    {return f[x]==x?x:f[x]=find(f[x]);}
    double dist(int x,int y)
    {return sqrt(sqr(dat[x].X-dat[y].X)+sqr(dat[x].Y-dat[y].Y));}
    
    int main()
    {
        scanf("%d%d",&n,&l);
        for(int i=1;i<=n;i++) 
            scanf("%d%d",&dat[i].X,&dat[i].Y);
        for(int i=0;i<=n+1;i++) f[i]=i;
        
        for(int i=1;i<=n;i++)
            add_edge(0,i,dat[i].Y),add_edge(i,n+1,l-dat[i].Y);
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                add_edge(i,j,dist(i,j));
        
        sort(e+1,e+tot+1,cmp);
        for(int i=1;i<=tot;i++)
        {
            int px=find(e[i].x),py=find(e[i].y);
            if(px==py) continue;f[px]=py;
            if(find(0)==find(n+1)) return printf("%.3lf",e[i].w),0;
        }
        return 0;
    }
    Problem B

    C:

    将0/1分别看作-1/1的贡献

    发现一个区间需要的最小修改次数为最大的不相交前/后缀和的和

    感性证明:

    最值:只要某位的前/后缀和大于零该位就一定要删除,因此该值为下界

    可行性:如果后面还有大于零的点则一定会向后拓展

    两种实现方式:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define mid ((l+r)>>1)
    #define lc k<<1,l,mid
    #define rc k<<1|1,mid+1,r
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=2e5+10;
    int n,m,l,r;char s[MAXN];
    struct node{int sum,lmx,rmx,mx;}seg[MAXN<<2];
    node operator + (node a,node b)
    {
        node ret;
        ret.sum=a.sum+b.sum;
        ret.lmx=max(a.lmx,a.sum+b.lmx);
        ret.rmx=max(b.rmx,b.sum+a.rmx);
        ret.mx=max(a.lmx+b.rmx,max(a.sum+b.mx,b.sum+a.mx));
        return ret;
    }
    
    void build(int k,int l,int r)
    {
        if(l==r) 
        {
            if(s[l]=='0') seg[k]=(node){-1,0,0,0};
            else seg[k]=(node){1,1,1,1};
            return;
        }
        build(lc);build(rc);
        seg[k]=seg[k<<1]+seg[k<<1|1];
    }
    node Query(int a,int b,int k,int l,int r)
    {
        if(a<=l&&r<=b) return seg[k];
        node ret=(node){0,0,0,0};
        if(a<=mid) ret=Query(a,b,lc);
        if(b>mid) ret=ret+Query(a,b,rc);
        return ret;
    }
    
    int main()
    {
        scanf("%d%d%s",&n,&m,s+1);
        build(1,1,n);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&l,&r),printf("%d
    ",Query(l,r,1,1,n).mx);
        return 0;
    }
    Solution A

    如果求$pre+suf$的最值要注意将区间向外拓展1

    (可能取$pre[l-1]$,也就是前缀不选)

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define mid ((l+r)>>1)
    #define lc k<<1,l,mid
    #define rc k<<1|1,mid+1,r
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P; 
    const int MAXN=8e5+10,INF=1<<28;
    char s[MAXN];
    int res,lft;
    int n,q,l,r,pre[MAXN],suf[MAXN];
    int mx[MAXN],lmx[MAXN],rmx[MAXN];
    
    void pushup(int k)
    {
        lmx[k]=max(lmx[k<<1],lmx[k<<1|1]);
        rmx[k]=max(rmx[k<<1],rmx[k<<1|1]);
        mx[k]=max(mx[k<<1],max(mx[k<<1|1],lmx[k<<1]+rmx[k<<1|1]));
    }
    
    void build(int k,int l,int r)
    {
        if(l==r)
        {
            lmx[k]=pre[l],rmx[k]=suf[l];
            mx[k]=-INF;return;
        }
        build(lc);build(rc);
        pushup(k);
    }
    
    void Query(int a,int b,int k,int l,int r)
    {
        if(a<=l&&r<=b)
        {
            if(lft==-INF)
                res=max(res,mx[k]),lft=lmx[k];
            else
                res=max(res,max(mx[k],lft+rmx[k])),
                lft=max(lft,lmx[k]);
            return;
        }
        if(a<=mid) Query(a,b,lc);
        if(b>mid) Query(a,b,rc);
    }
    
    int main()
    {
        scanf("%d%d%s",&n,&q,s+1);
        for(int i=1;i<=n;i++)
            pre[i]=pre[i-1]+(s[i]=='0'?-1:1);
        for(int i=n;i>=1;i--)
            suf[i]=suf[i+1]+(s[i]=='0'?-1:1);
        build(1,0,n+1);
        
        while(q--)
        {
            scanf("%d%d",&l,&r);
            res=lft=-INF;l--;r++;
            Query(l,r,1,0,n+1);
            printf("%d
    ",res-pre[l]-suf[r]);
        }
        return 0;
    }
    Solution B

    针老师题解里的树上倍增可能不太懂啊……

  • 相关阅读:
    打开项目遇到Unknown Android Packaging Problem问题
    C# 水波效果
    Adding controls to ToolStrip in C#
    C# 水波效果
    【转】U盘启动奶瓶破解无线WPA加密
    如何破解ROS路由器禁用路由PPPOE拨号?
    打开项目遇到Unknown Android Packaging Problem问题
    各版本.NET委托的写法回顾
    Oracle起动库时1102报错处置
    Windows环境中Kill失落Oracle线程
  • 原文地址:https://www.cnblogs.com/newera/p/9667665.html
Copyright © 2011-2022 走看看