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\n",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\n", len);
    for(int i = 0; i < len; i++){
        printf("%d%c",ans[i]+1,i+1 == len?'\n' : ' ');
    }
}

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。

猜你喜欢

转载自www.cnblogs.com/PaperCloud/p/9032919.html
今日推荐