zoukankan      html  css  js  c++  java
  • 【杂题1】USACO 2018 Open Contest-练习

    https://www.xoj.red/contests/show/1231

    下面会写一些题目的解析什么的,当然不会粘贴题目只是简单提一下

     (部分题目简单的题目就不概括了)

    其实难度应该前面比较低.

    问题A: 3894: Out of Sorts

    本题求出冒泡排序需要几趟。

    考虑一次冒泡排序的交换,减小对应1个位子上的1个逆序对。

    但是对于每一个位子所需要减小的逆序对数量是不一样的。

    对于每一趟,消去每一个位子上1个逆序对
    所以趟数就是每个位子上的数产生逆序对数的最大值。

    最后的+1指的是即使上一次已经消除所有逆序对了,我们并不知道数组有序了,所以判断最后一遍查看是否有序。

    考虑树状数组维护$O(n log_2 n)$

    和善的树状数组代码如下:

    # include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    int a[N],tmp[N],c[N];
    int n,T;
    void update(int x){for (;x<=n;x+=x&-x)c[x]++;}
    int query(int x){int ret=0;for (;x;x-=x&-x) ret+=c[x];return ret;}
    int main()
    {
        scanf("%d",&n); tmp[0]=n;
        for (int i=1;i<=n;i++) scanf("%d",&a[i]),tmp[i]=a[i];
        sort(tmp+1,tmp+tmp[0]+1);
        T=unique(tmp+1,tmp+1+tmp[0])-tmp-1;
        int ans=0;
        for (int i=1;i<=n;i++) {
            int w=lower_bound(tmp+1,tmp+1+T,a[i])-tmp;
            update(w);
            ans=max(i-query(w),ans);
        }
        printf("%d
    ",ans+1);
        return 0;
    }
    问题A: 3894: Out of Sorts

    问题B: 3895: Lemonade Line

    考虑一个简单的贪心,每次选择当前等待奶牛数目最多的一个,占用等待奶牛数少的奶牛的位置。

    如果不是这样做,对答案会有正贡献,是我们不期望的,与答案最优性矛盾,我们选择一个一定是最优的决策。

    # include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    int n,a[N];
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        int cnt=0,Ans=0;
        for (int i=n;i>=1;i--) {
            if (cnt<=a[i])Ans++,cnt++;
            else break;
        }
        printf("%d
    ",Ans);
        return 0;
    }
    问题B: 3895: Lemonade Line

     问题C: 3896: Multiplayer Moo

    大概是不存在一个正解的。

     问题D: 3897: Out of Sorts

    考虑这样一个事情,如果正反来了一边那么每一次对于i位置上的数a[i]来说他左边位置j $in $ [1,i-1]比a[i]严格大的数有一个被放到了i的右边

    他右边的位置j$in $ [i+1,n]比a[i]严格小的数有一个被放到了i的左边。当且仅当a[i]左边和右边不存在上述的数那么停止。

    如果把一个数组变为有序显然第i个位子上放置第i大的数。

    显然他们的位置一定在i位置后面。所有至少需要移动这么多次,把每一个比i大的数移动到i的右面。

    显然我们冒泡1次对于每个i对应的有多少数在他左边只能减少1个这样的数,所以答案就是max{前i个数里面有多少个数字离散化后是比i大的,1}

    # include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    int c[N],n;
    struct rec{int x,id;}a[N];
    bool cmp(rec a,rec b)
    {
        if (a.x!=b.x) return a.x<b.x;
        return a.id<b.id;
    }
    void update(int x){for (;x<=n;x+=x&-x) c[x]++;}
    int query(int x){int ret=0; for (;x;x-=x&-x) ret+=c[x]; return ret;}
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
         scanf("%d",&a[i].x),a[i].id=i;
        sort(a+1,a+1+n,cmp);
        int ans=1;
        for (int i=1;i<=n;i++){
            update(a[i].id);
            ans=max(ans,i-query(i));
        }
        printf("%d
    ",ans);
        return 0;
    }
    问题D: 3897: Out of Sorts

     问题E: 3898: Milking Order

    本题要求最大化pos满足前面若干条边连起来是一个无环图。

    求最优遍历顺序,想到拓扑排序check+二分答案,然后手打堆优化STL常数。

    先存输入,每一次二分答案位置pos,把前面pos个连边系统前面连到后面,然后跑拓扑,每次选择入度为0,标号最小的一个遍历

    这里用到heap了,然后使用priority_queue在不开O2情况下TLE,然后就写了一个手写堆。

    # include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+10,M=2e5+10;
    int head[N],ind[N],ans[N];
    bool vis[N];
    vector<int>ipt[N];
    int n,m,tot;
    struct rec{int pre,to;}a[M];
    inline int read()
    {
        int X=0,w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    }
    inline void write(int x)
    {
        if (x>9) write(x/10);
        putchar('0'+x%10);
    }
    void adde(int u,int v)
    {
        ind[v]++;
        a[++tot].pre=head[u];
        a[tot].to=v;
        head[u]=tot;
    }
    struct heap{
        # define lson (x<<1)
        # define rson ((x<<1)+1)
        # define fath (x>>1)
        int Size,c[N<<1];
        heap(){Size=0;}
        void down(int x) {
             while (x<Size) {
                 if (lson>Size) break;
                 int t=lson;
                 if (rson<=Size&&c[rson]<c[lson]) t=rson;
                 if (c[x]<=c[t]) break;
                 swap(c[x],c[t]);
                 x=t;
             }
        }
        void up(int x) {
            while (x) {
                if (c[x]>c[fath]) break;
                swap(c[x],c[fath]);
                x=fath;
            }
        }
        int top(){return c[1];}
        void push(int x){c[++Size]=x;up(Size);}
        void pop() {swap(c[1],c[Size--]);down(1);}
        bool empty(){ return !Size;}
    }q;
    bool check(int Mid)
    {
        tot=0;
        memset(vis,false,sizeof(vis));
        memset(head,0,sizeof(head));
        memset(ind,0,sizeof(ind));
        for (int i=1;i<=Mid;i++)
         for (int j=1;j<ipt[i].size();j++)
          adde(ipt[i][j-1],ipt[i][j]);
        for (int i=1;i<=n;i++)
         if (ind[i]==0) q.push(i);
        ans[0]=0;
        while (!q.empty()) {
            int u=q.top(); q.pop();
            vis[u]=true;
            ans[++ans[0]]=u;
            for (int i=head[u];i;i=a[i].pre){
                int v=a[i].to;
                if (ind[v]==0) return false;
                ind[v]--;
                if (ind[v]==0) q.push(v);
            }
        }
        for (int i=1;i<=n;i++)
            if (!vis[i]) return false;
        return true;
    }
    int main()
    {
        n=read();m=read();
        for (int i=1;i<=m;i++) {
            int r=read();
            for (int j=1;j<=r;j++) {
                int t=read();
                ipt[i].push_back(t);
            }
        }
        int L=1,R=m,Ans=0;
        while (L<=R) {
            int mid=(L+R)>>1;
            if (check(mid)) Ans=mid,L=mid+1;
            else R=mid-1;
        }
        if (Ans==0) {
            for (int i=1;i<=n;i++) write(i),putchar(' ');
            putchar('
    ');
            return 0;
        }
        check(Ans);
        for (int i=1;i<=n;i++) write(ans[i]),putchar(' ');
        putchar('
    ');
        return 0;
    }
    问题E: 3898: Milking Order

     问题F: 3899: Talent Show

    考虑01分数规划,即二分答案套dp检查。

    设答案$xleq frac{sum w_i}{sum t_i} $移项可得$sum t_i - x sum w_i geq 0$

    即要求我们对于给定参数x,求一组$w_i$和$t_i$满足$sum t_i - x sum w_i geq 0$

    即$max{ sum t_i - x sum w_i } geq 0$

    对于第i头奶牛的贡献就变成一个确定的数了,即$t_i-xw_i$

    虑01背包。

    设f[x]表示当前选择总重量为x,$sum t_i - x sum w_i $的最大值。

    考虑从x转移出去对x+w[i]有影响,有转移方程$f[x+w[i]]=max(f[x+w[i]],f[x]+t_i-xw_i)$

    然后要求总重量超过W,事实上我们并不关心到底超过W多少,只要超过就行,那么就统计在恰好的地方

    即统计在f[W]处,那么f[W]就是答案。

    # include <bits/stdc++.h>
    # define inf (1<<30)
    using namespace std;
    const int N=1e5+10;
    double f[N];
    int w[N],t[N];
    int n,W;
    bool check(double x)
    {
        f[0]=0;
        for(int i=1;i<=W;i++) f[i]=-inf;
        for (int i=1;i<=n;i++)
         for (int j=W;j>=0;j--)
          f[min(j+w[i],W)]=max(f[min(j+w[i],W)],f[j]+t[i]-x*w[i]);
        return f[W]>=0.0;
    }
    int main()
    {
        scanf("%d%d",&n,&W);
        for (int i=1;i<=n;i++)
         scanf("%d%d",&w[i],&t[i]);
        double l=0,r=inf,ans;
        while (r-l>=(1e-5)){
            double mid=(l+r)/2.0;
            if (check(mid)) ans=mid,l=mid;
            else r=mid;
        }
        ans=ans*1000;
        printf("%d
    ",(int)ans);
        return 0;
    }
    问题F: 3899: Talent Show

     问题G: 3900: Out of Sorts

    我们不妨宏观考虑这个排序的问题,在对于A进行子冒泡排序的时候,可以发现,比第i个元素小的元素且在i右边的元素是以1贡献/次向i移动的。

    所以第i个元素和第i+1元素之间的隔板产生的区间长度就是$ (maxpos-i+1-1)=maxpos-i $,其中$ maxpos $指的是排序以后比$a_i$元素更小的,但排在$a_i$元素的右边,且距离$a_i$最远的数$a_j$的下标(位置)$j$。

    对于i被计算1次当他的前面隔板不是连续的,或者 后面隔板不是连续的满足其一即可,所以若把i存在于的这个隔板不连续的区间排成有序,那么仅对于元素i它被算的次数(产生的贡献)是$max(t_{i-1},t_i)$

    说明一下: 如果i前后隔板都不连续的话i被产生$max(t_{i-1},t_i)$后便不产生贡献了,如果前面或后面隔板不连续显然。

    有了上面的说明,这个题目的算法呼之欲出,

     1. 遍历原数组,当前位置为i,求出$ t_i $,查找最后一个比$a_i$小的数出现的位置为j,令$ t_i = j - i $

     2. 求出 $ans = sumlimits_{i=1} ^ {n} max { t_{i-1}, t_i }$ 并输出。

     显然按照上面模拟是$ O(n^2) $算法。

     我们可以这样做,先把$a_i$每个元素记录它的值$val$和下标$i$,排序。

     那么求一个前缀$ maxpos=max{a[i].id , maxpos }$,显然遍历到i位置的时候,前面都是比$ a[i] $ 的值,然后前面最大的$ id $号码就是$ maxpos $。

     那么时间复杂度就降到$ O(nlog_2n) $

    # include <bits/stdc++.h>
    # define int long long
    using namespace std;
    const int N=1e5+10;
    struct rec{ int val,id; }a[N];
    int n,t[N];
    bool cmp(rec a,rec b)
    {
        if (a.val!=b.val) return a.val<b.val;
        else return a.id<b.id;
    }
    signed main()
    {
        scanf("%lld",&n);
        for (int i=1;i<=n;i++)
         scanf("%lld",&a[i].val),a[i].id=i;
        sort(a+1,a+1+n,cmp);
        int maxpos=0;
        for (int i=1;i<=n;i++) {
            maxpos=max(maxpos,a[i].id);
            t[i]=max(1ll,maxpos-i);
        } 
        int ans=0;
        for (int i=1;i<=n;i++)
         ans+=max(t[i],t[i-1]);
        cout<<ans; 
        return 0;
    }
    问题G: 3900: Out of Sorts

     问题I: 3902: Disruption

     这个题目非常有价值,我想专门开一个blog讲一讲这个题。

     https://www.cnblogs.com/ljc20020730/p/10467324.html 

    欢迎支持!!!

  • 相关阅读:
    Android RecyclerView如何去掉上拉刷新和下拉加载的阴影
    python如何在列表、对象、集合中根据条件筛选数据
    解决Glide在一个imageview上更换图片时会闪的问题
    响应时间三个非常重要的时间点
    android 向系统日历添加日程事件
    android 系统日历 插入重复事件规则 RRULE
    android Calendar Provider
    android edittext设置获取焦点
    android 自定义光标颜色
    android动态改变TextView字体大小遇到的问题
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/10458156.html
Copyright © 2011-2022 走看看