zoukankan      html  css  js  c++  java
  • 20180513模拟赛

    T1/Codeforces 975C

    很简单,直接前缀和然后二分找答案即可,注意超过最大挡箭数的候会“全员复活” ······

    #include<bits/stdc++.h>
    using namespace std;
    inline long long read(){
        long long x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return x*f;
    }
    #define MN 200005
    int n,q;
    long long a[MN],k[MN];
    int main(){
        n=read(),q=read();
        for(int i=1;i<=n;i++) a[i]=read()+a[i-1];
        for(int i=1;i<=q;i++) k[i]=read(),k[i]=k[i]+k[i-1]>=a[n]?0:k[i]+k[i-1];
        for(int i=1;i<=q;i++){
            cout<<n-(upper_bound(a+1,a+n+1,k[i])-a-1)<<endl;
        }
        return 0;
    }

     T2/POJ 1704

    ~博弈论,题意是有一行的棋格,Bob和Georgia比赛玩,没人每次可以把任意棋子像左边移动任意的长度,问先手胜还是后手胜。

    怎么说,这是最基础的博弈论了吧,把相邻的两个棋子分成同一组,将它们之间的间隔当作SG值,直接^在一起,当结果为0时是必败态,否则是必胜态。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return x*f;
    }
    #define MP 10005
    int a[MP],n,T;
    int main(){
        T=read();
        while(T--){
            n=read();
            for(int i=1;i<=n;i++) a[i]=read();
            sort(a+1,a+n+1);
            int sum=0;
            for(int i=n;i>0;i-=2) sum^=a[i]-a[i-1]-1;
            printf("%s
    ",sum?"Georgia will win":"Bob will win");
        }
        return 0;
    }

    T3/POJ 3709

    ~斜率优化。

    题意:给你一堆数,你可以随意的减小它们数值,使得每个数的出现次数不少于k的情况下,问最小的代价是多少。

    其实也很少打斜率的题,而且dp本来就是我的弱项,能够A掉这道题,只能说是lucky吧。。。

    显然可以得到如下递推式:
           f[i]=min{f[j]+a[j+2]-a[j+1]+a[j+3]-a[j+1]+...+a[i]-a[j+1],0<=j<=i-k+1} O(n^3) 
    用前缀和优化,得到:
      f[i]=min{f[j]+S[i]-S[j]-(i-j)*a[j+1],0<=j<=i-k+1} O(n^2)

    考虑对于两个数 x 和 y ,(x<y),如果x优于y,我们必须要满足的条件是:

      f[x]-S[x]-(i-x)*a[x+1]<f[y]-S[y]-(i-y)*a[y+1]

    整理可得:

      f[x]-f[y]-S[x]+S[y]+x*a[x+1]-y*a[y+1]<i*(a[x+1]-a[y+1])

    所以呢,随着i的增加,等式右边的值会越来越小,队首的元素可能会不再优于后面的元素,所以我们需要先排除队首的元素问题:

    ll get(int x){
        while(top>tail&&dy(q[tail],q[tail+1])>x*dx(q[tail],q[tail+1])) tail++;
        return q[tail];
    }

    然后我们继续,此时的f[i]值等于,当j等于队首元素值时上式的值。

    单调队列的进队操作很显然,令对尾两数为top-1 和 top,将入队的为x,如果x优于top的程度比top优于top-1的程度还厉害,即x会比top更早的优于前一个元素,那么直接--top吧。

    void ins(int x){
        while(top>tail&&dy(q[top-1],q[top])*dx(q[top],x)>=dy(q[top],x)*dx(q[top-1],q[top]))--top;
        q[++top]=x;
    }

    本质上呢,就是一个斜率优化。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return x*f;
    }
    #define MN 500005
    #define ll long long
    ll T,k,n,f[MN];
    struct sq{ll sm,a;}l[MN];
    ll top,tail,q[MN];
    ll dx(int x,int y){return l[x+1].a-l[y+1].a;}
    ll dy(int x,int y){return f[x]-f[y]-l[x].sm+l[y].sm+x*l[x+1].a-y*l[y+1].a;}
    ll get(int x){
        while(top>tail&&dy(q[tail],q[tail+1])>x*dx(q[tail],q[tail+1])) tail++;
        return q[tail];
    }
    void ins(int x){
        while(top>tail&&dy(q[top-1],q[top])*dx(q[top],x)>=dy(q[top],x)*dx(q[top-1],q[top]))--top;
        q[++top]=x;
    }
    int main(){
        T=read();
        while(T--){
            n=read(),k=read();
            for(int i=1;i<=n;i++) l[i].a=read(),l[i].sm=l[i].a+l[i-1].sm;
            q[top=tail=1]=0;
            for(int i=1;i<=n;i++){
                int from=get(i),to=i-k+1;
                f[i]=f[from]+l[i].sm-l[from].sm-1LL*(i-from)*l[from+1].a;
                if(to>=k) ins(to);
            }
            cout<<f[n]<<endl;
        }
        return 0;
    }

    T4/POJ 2932

    题目的意思很好懂吖。一些不会相交也不相切的圆,找到所有不内含于其他圆的圆。

    因为题目内的圆不存在相交的情况, 所以直接储存每个圆的左端点和右端点的x坐标,然后从左扫到右。

    我们在从左向右平移与y轴平行的直线的同时,维护与扫面线相交的最外层的圆的集合。从左到右移动中,只有扫面线移动到圆的左右两端时,

    圆与扫描线的相交关系才会发生变化,因此我们先将所有这样的x坐标枚举出来并排好序。如果满足是最外面的圆,就储存在set里面。

    如何判断满足呢,就是对每一个x,如果是左端点的x坐标就来判断其对应的圆,是否是在set中储存的圆内,如果不是 ,就存到set中。如果是右端点的x坐标,就pop出该圆。

    好像:是不是很难呢?

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <set>
    #include <algorithm>
    using namespace std;
    
    const int N = 40005;
    int n;
    double x[N],y[N],r[N];
    
    bool inside(int i, int j){
        double dx = x[i]-x[j];
        double dy = y[i]-y[j];
        return dx*dx+dy*dy <= r[j]*r[j];
    }
    
    void solve(){
        vector<pair<double,int> > events;//圆的左右两端的x坐标
        for(int i = 0; i < n; i++){
            events.push_back(make_pair(x[i]-r[i],i));//圆的左端
            events.push_back(make_pair(x[i]+r[i],i+n));//圆的右端
        }
        sort(events.begin(), events.end());
        //平面扫描
        set<pair<double, int> > outers;//与扫面线相交的最外层的圆的集合
        vector<int>ans;//最外层圆的列表
        for(int i = 0; i < events.size(); i++){
            int id = events[i].second % n;
            if(events[i].second < n){//扫描到左端
                set<pair<double, int> >::iterator it = outers.lower_bound(make_pair(y[id],id));
                if(it != outers.end() && inside(id, it->second)) continue;
                if(it != outers.begin() && inside(id, (--it)->second)) continue;
                ans.push_back(id);
                outers.insert(make_pair(y[id], id));
            }
            else{//扫描到右端
                outers.erase(make_pair(y[id], id));
            }
        }
        sort(ans.begin(), ans.end());
    
        int len = ans.size();
        printf("%d
    ", len);
        for(int i = 0; i < len; i++){
            printf("%d%c",ans[i]+1,i+1 == len?'
    ' : ' ');
        }
    }
    
    int main(){
        while(~scanf("%d",&n)){
            for(int i = 0; i < n; i++)
                scanf("%lf%lf%lf", &r[i],&x[i],&y[i]);
            solve();
        }
        return 0;
    }

    At last,

    happy rank 1!


    来自PaperCloud的博客,未经允许,请勿转载,TKS。

  • 相关阅读:
    学习vim命令:“:w !sudo tee %”
    mac下安装和卸载软件
    很好用的在线markdown编辑器
    doc2vec 利用gensim 生成文档向量
    C语言经典算法100例-024-求数列的前20 项和,2/1,3/2,5/3,8/5...
    C语言经典算法100例-023-打印菱形
    C语言经典算法100例-022-乒乓球比赛名单问题
    C语言经典算法100例-021-猴子吃桃问题
    C语言经典算法100例-020-小球自由下落问题
    C语言经典算法100例-019-求完数
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/9032919.html
Copyright © 2011-2022 走看看