NOIP2012复赛DAY2

来源:互联网 发布:游戏 建模 软件 编辑:程序博客网 时间:2024/05/22 09:51

NOIP2012——DAY2

1、同余方程

【题目分析】
其实也不用分析了,这道题是作为我们的数论入门题来练的。如果在考场上碰到这种恶心的数学题,不管敲得对敲不对,反正一定要把暴力先敲好。
http://blog.csdn.net/ycdfhhc/article/details/44260687
作为一个蒟蒻,想解释但还是心有余而力不足啊。不懂的小伙伴自行学习,早就懂的小伙伴也可以温习一下。

【代码】

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#define ll long longusing namespace std;ll ex_gcd(ll a,ll b,ll &x,ll &y){    ll d=a;    if(b){        d=ex_gcd(b,a%b,y,x);        y-=a/b*x;    }else x=1,y=0;    return d;}int main(){    ll a,b,x,y;    cin>>a>>b;    int ans=ex_gcd(a,b,x,y);    while(x<=0)x+=b;    cout<<x<<endl;    return 0;}

2、借教室

【题目分析】
都DAY2了,还能不能让我愉快地敲一个暴力?
不多说,45分暴力代码先给出。

【45分代码】

#define M 1000005#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;int cnt[M];int main(){    int i,j,n,m;    scanf("%d %d",&n,&m);    for(i=1;i<=n;i++)scanf("%d",&cnt[i]);    for(i=1;i<=m;i++){        int d,l,r;        scanf("%d %d %d",&d,&l,&r);        for(j=l;j<=r;j++){            cnt[j]-=d;            if(cnt[j]<0)break;        }if(j!=r+1)break;    }    if(i==m+1)puts("0");    else puts("-1"),printf("%d\n",i);    return 0;}

枚举->贪心,貌似都不行。
我们的目标是完成所有订单。
题目里虽然没有说最大最小,之类的话,只说了,“如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配”,如果这一份订单如果无法完成,那么我们假设的可以完成所有订单这一目标也一定无法完成。
如果先定一个小目标,看他能不能完成。如果小目标不能完成,大目标也一定不能完成。所以,具有单调性。
思路就变成了二分答案。
同时,在假设当前的目标能够完成的时候,一个一个减太慢了,我们学过差分前缀和(也就是刷漆),将每次查询的复杂度降到了O(n)。
复杂度O(n*logn)。

【代码】

#include<cmath>#include<cstdio>#include<cstring>#include<cstdlib>#define M 1000005#include<iostream>#include<algorithm>using namespace std;void Rd(int &res){    char c;res=0;    while(c=getchar(),!isdigit(c));    do{res=(res<<3)+(res<<1)+(c^48);}while(c=getchar(),isdigit(c));}struct quertion{int l,r,d;}q[M];int n,m,ans=0,a[M];long long b[M];bool chk(int x){    bool f=1;    long long sum=0;    memset(b,0,sizeof(b));    for(int i=1;i<=x;i++){        b[q[i].l]+=q[i].d;        b[q[i].r+1]-=q[i].d;    }    for(int i=1;i<=n;i++){        sum+=b[i];        if(sum>a[i])return 0;    }return 1;}int main(){    int i;    Rd(n),Rd(m);    for(i=1;i<=n;i++)Rd(a[i]);    for(i=1;i<=m;i++)Rd(q[i].d),Rd(q[i].l),Rd(q[i].r);    int l=1,r=m;    while(l<=r){        int mid=l+r>>1;        if(!chk(mid))ans=mid,r=mid-1;        else l=mid+1;    }    if(!ans)puts("0");    else printf("-1\n%d\n",ans);    return 0;}

天啊撸,刚打好的疫情控制题解没了,都怪一个叫yahong的变态!!

3、疫情控制

【题目分析】
从枚举军队最后的状态入手,可以水到20分。
需要将有限的军队最大的发挥它们的作用,就要不断向上攀爬,使他能满足更多的边疆城市,这是贪心的思想。
从常识中可以知道,若是疫情被能控制,时间越长,更有可能。而我们不可能按照时间的推移来检验该时间是否可行(显然超时),又有“请问最少需要多少个小时才能控制疫情”,得出二分答案的做法。
但是首都的儿子中有些点是本来就没有军队的,所以将军队又分为了需要翻过首都支援的和留在原地的。而我们又希望剩余时间更多的来支援,这还是贪心的思想。
接下来就是如何实现的问题了。

【代码】

#define M 50005#include<queue>#include<cstdio>#include<vector>#include<cstring>#include<iostream>#include<algorithm>#define ll long long#define oo 1e15using namespace std;void Rd(int &res){    res=0;char c;    while(c=getchar(),!isdigit(c));    do res=(res<<3)+(res<<1)+(c^48);    while(c=getchar(),isdigit(c));}struct node{int v,w,nxt;}st[M<<1];int fa[M],army[M],head[M],rest[M];int n,m,etop=0;bool mk[M];ll dis[M];void add_edge(int u,int v,int w){    st[++etop]=(node){v,w,head[u]},head[u]=etop;    st[++etop]=(node){u,w,head[v]},head[v]=etop;}void dfs(int u,int pre,ll d){    dis[u]=d,fa[u]=pre;    for(int j=head[u];~j;j=st[j].nxt){        node now=st[j];        if(now.v!=pre)dfs(now.v,u,d+now.w);    }}struct cmp{bool operator()(int &a,int &b)const{return dis[a]<dis[b];}};bool check(int u,int pre,bool flag){    if(flag)return 1;    bool f=0;    for(int j=head[u];~j;j=st[j].nxt){        int v=st[j].v;        if(v==pre)continue;        f=1;        if(!check(v,u,flag|mk[v]))return 0;    }return f;}bool check(ll T){    memset(mk,0,sizeof(mk));    memset(rest,-1,sizeof(rest));    priority_queue<int>q1;    priority_queue<int,vector<int>,cmp>q2;    for(int i=1;i<=m;i++){        int u=army[i];        while(fa[u]&&fa[u]!=1&&dis[army[i]]-dis[fa[u]]<=T)u=fa[u];        if(fa[u]!=1||T<dis[army[i]])mk[u]=1;        else{            int v=T-dis[army[i]];            if(rest[u]==-1)rest[u]=v;            else{                if(rest[u]>v)swap(rest[u],v);                q1.push(v);            }        }    }    for(int j=head[1];~j;j=st[j].nxt){        int v=st[j].v;        if((~rest[v])&&(check(v,1,mk[v])||rest[v]>=dis[v]))q1.push(rest[v]);        else if(~rest[v])mk[v]=1;    }    for(int j=head[1];~j;j=st[j].nxt){        int v=st[j].v;        if(!check(v,1,mk[v]))q2.push(v);    }    while(!q1.empty()&&!q2.empty()){        int v=q2.top();        if(q1.top()>=dis[v]){            mk[v]=1;            q1.pop();            q2.pop();        }else q1.pop();    }return check(1,0,0);}int main(){    Rd(n);    int cnt=0;    memset(head,-1,sizeof(head));    for(int i=1,u,v,w;i<n;i++){        Rd(u),Rd(v),Rd(w);        if(u==1||v==1)cnt++;        add_edge(u,v,w);    }Rd(m);    for(int i=1;i<=m;i++)Rd(army[i]);    if(cnt>m){puts("-1");return 0;}    dfs(1,0,0);    int l=0;    ll r=oo;    ll ans=-1;    while(l<=r){        int md=l+r>>1;        if(check(md))ans=md,r=md-1;        else l=md+1;    }cout<<ans<<endl;    return 0;}

虽然能过,但是有点慢,是因为想偷点懒,所以在向上的时候没有“跳”。

【思路】
1、写过了。
2、枚举->二分
3、枚举->贪心->二分答案

总的来说,如果有恶心的数学题,还是乖乖地先去敲出暴力再去想正解吧,虽然说可能性并不大。同时二分答案也是解题的一个思路之一,在想问题的时候一定要往上面去想一想,套一套。还有就是第三题,有了思路并不代表有了一切,最终还是要把它实现出来啊。

2 0
原创粉丝点击