NOIP2012 Day2

来源:互联网 发布:js 正则 可有可无 编辑:程序博客网 时间:2024/06/05 00:58

T1 同余方程

套一个exgcd模板即可

#include<bits/stdc++.h>using namespace std;int ex_gcd(int a,int b,int &x,int &y){    if (!b){        x=1; y=0;        return a;    }    else{        int t=ex_gcd(b,a%b,y,x);        y=y-a/b*x;        return t;    }}       int main(){    int a,b,x,y;    scanf("%d%d",&a,&b);    ex_gcd(a,b,x,y);    printf("%d\n",x<0?x%b+b:x);    return 0;}

T2 借教室

解法1

比较裸的区间更新 区间查询线段树
O(mlogn)
注意卡常 毕竟是106的数据

#include<bits/stdc++.h>using namespace std;#define N 1000010void rd(int &x){    char c;x=0;    while (c=getchar(),c<48);    do x=(x<<1)+(x<<3)+(c^48);    while (c=getchar(),c>=48);}int a[N];struct Segment_Tree{    #define Lc p<<1    #define Rc p<<1|1    int add[N<<2],Mn[N<<2];    void up(int p){        Mn[p]=min(Mn[Lc],Mn[Rc]);    }    void down(int p){        add[Lc]+=add[p];        add[Rc]+=add[p];        Mn[Lc]-=add[p];        Mn[Rc]-=add[p];        add[p]=0;    }    void build(int l,int r,int p){        if (l==r){            Mn[p]=a[l];            return;        }        int mid=(l+r)>>1;        build(l,mid,Lc);        build(mid+1,r,Rc);        up(p);    }    void update(int l,int r,int p,int pl,int pr,int x){        if (l==pl && r==pr){            add[p]+=x;            Mn[p]-=x;            return;        }        int mid=(l+r)>>1;        down(p);        if (pr<=mid) update(l,mid,Lc,pl,pr,x);        else if (pl>mid) update(mid+1,r,Rc,pl,pr,x);        else update(l,mid,Lc,pl,mid,x),update(mid+1,r,Rc,mid+1,pr,x);        up(p);    }    int query(int l,int r,int p,int pl,int pr){        if (l==pl && r==pr) return Mn[p];        int mid=(l+r)>>1;        down(p);        if (pr<=mid) return query(l,mid,Lc,pl,pr);        else if (pl>mid) return query(mid+1,r,Rc,pl,pr);        else return min(query(l,mid,Lc,pl,mid),query(mid+1,r,Rc,mid+1,pr));    }}T;int main(){    int n,m,i;    rd(n),rd(m);    for (i=1;i<=n;i++) rd(a[i]);    T.build(1,n,1);    for (i=1;i<=m;i++){        int x,l,r;        rd(x),rd(l),rd(r);        if (T.query(1,n,1,l,r)<x){            printf("-1\n%d\n",i);            return 0;        }        T.update(1,n,1,l,r,x);    }    printf("0\n");    return 0;}

解法2

考虑二分恰好不合法的那个位置
判断时只需用差分前缀和维护每个时间借的教室的个数
同样O(mlogn)但常数小于线段树

#include<bits/stdc++.h>using namespace std;#define N 1000010void rd(int &x){    char c;x=0;    while (c=getchar(),c<48);    do x=(x<<1)+(x<<3)+(c^48);    while (c=getchar(),c>=48);}int n,m,a[N],p[N],l[N],r[N];long long sum[N];bool check(int x){    int i;    memset(sum,0,sizeof(sum));    for (i=1;i<=x;i++){        sum[l[i]]+=p[i];        sum[r[i]+1]-=p[i];    }    long long tmp=0;    for (i=1;i<=n;i++){        tmp+=sum[i];        if (tmp>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(p[i]),rd(l[i]),rd(r[i]);    if (check(m)){        printf("0\n");        return 0;    }    int l=1,r=m,res=m;    while (l<=r){        int mid=(l+r)>>1;        if (!check(mid)) res=mid,r=mid-1;        else l=mid+1;    }    printf("-1\n%d\n",res);    return 0;}

解法3

可以用每次不合法就撤销一些操作的方法来实现
同样用差分前缀和维护
如果当前点不满足 就从后向前撤销操作 直到满足为止
看最后剩下的操作编号 +1即为不满足的编号
O(n+m)

#include<bits/stdc++.h>using namespace std;#define N 1000010void rd(int &x){    char c;x=0;    while (c=getchar(),c<48);    do x=(x<<1)+(x<<3)+(c^48);    while (c=getchar(),c>=48);}int a[N],p[N],l[N],r[N];long long sum[N];int main(){    int i,n,m;    rd(n),rd(m);    for (i=1;i<=n;i++) rd(a[i]);    for (i=1;i<=m;i++){        rd(p[i]),rd(l[i]),rd(r[i]);        sum[l[i]]+=p[i];        sum[r[i]+1]-=p[i];    }    long long now=0;    int t=n;    for (i=1;i<=n;i++){        now+=sum[i];        while (now>a[i]){            sum[l[t]]-=p[t];            sum[r[t]+1]+=p[t];            if (l[t]<=i && r[t]>=i) now-=p[t];            t--;        }    }    if (t==n) printf("0\n");    else printf("-1\n%d\n",t+1);    return 0;}

T3 疫情控制

如果已知最大时间 一个军队肯定尽量向上跳 因为这样可以覆盖更多的点
那么可以二分最大时间 用倍增求最多跳到的点

但是有一种情况 就是一个子树内的点跑到另一个子树内
可以先把能跑到1的军队处理出来 然后贪心处理
如果当前子树已经可以被其他的军队完全覆盖 那就可以让到1的军队全部出去
否则先留下剩余时间最少的一个

然后把还未覆盖的点按到1的时间排序 同时用一个小顶堆维护到1的军队的剩余时间
每次用小的匹配小的
如果当前点有留下军队 就要比较当前的堆顶和留下的军队 选取剩余时间少的用掉 多的放回堆内
在堆中如果堆顶时间不够跑 就弹出

有两种情况
1:堆顶军队剩余时间>留下的军队剩余时间 这个时候用掉留下的军队 肯定不会对后面造成影响 因为留下的跑不到后面去
2:堆顶军队剩余时间留下的军队剩余时间 这个时候用堆顶的军队 把留下的放进去 这样留下的军队会在后面做贡献
因此这个贪心是正确的
O(nlog2n)

#include<bits/stdc++.h>using namespace std;#define N 50010#define K 16#define INF (0x3f3f3f3f3f3f3f3f)void rd(int &x){    char c;x=0;    while (c=getchar(),c<48);    do x=(x<<1)+(x<<3)+(c^48);    while (c=getchar(),c>=48);}struct edge{    int nxt,t,s;}e[N<<1];int head[N],edge_cnt,fa[N][K],n,m,a[N],top[N],Head[N];long long dis[N][K],f[N];void add_edge(int x,int y,int z){    e[edge_cnt]=(edge){head[x],y,z};    head[x]=edge_cnt++;}struct node{    int id,s;    bool operator <(const node &_)const{        return s<_.s;    }}Q1[N];struct poi{    int nxt;    long long t;}A[N];struct heap{    int h;    long long a[N];    void ins(long long x){        a[++h]=x;        int i=h;        while (i>1){            if (a[i>>1]>a[i]) swap(a[i>>1],a[i]),i>>=1;            else break;        }    }    void del(){        a[1]=a[h--];        int i=1;        while ((i<<1)<=h){            int j=i<<1;            if (j+1<=h && a[j+1]<a[j]) j++;            if (a[i]>a[j]) swap(a[i],a[j]),i=j;            else break;        }    }}Q;bool b[N];long long Mx;void dfs(int x,int f,int tp,long long Dis){    Mx=max(Mx,Dis);    fa[x][0]=f;    top[x]=tp;    int i;    for (i=head[x];~i;i=e[i].nxt){        int to=e[i].t,val=e[i].s;        if (to==f) continue;        dis[to][0]=val;        dfs(to,x,!tp?to:tp,Dis+val);    }}long long init(){    dfs(1,0,0,0);    int i,j;    for (i=1;i<K;i++)        for (j=1;j<=n;j++){            int x=fa[j][i-1];            fa[j][i]=fa[x][i-1];            dis[j][i]=dis[j][i-1]+dis[x][i-1];        }    int Mx1=0;    for (i=head[1];~i;i=e[i].nxt) Mx1=max(Mx1,e[i].s);    return Mx+Mx1;}bool filled(int x,int f){    if (b[x]) return 1;    else if (e[head[x]].nxt==-1) return 0;    int i;    for (i=head[x];~i;i=e[i].nxt){        int to=e[i].t;        if (to==f) continue;        if (!filled(to,x)) return 0;    }    return 1;}bool check(long long t){    int i,j;    memset(b,0,sizeof(b));    for (i=head[1];~i;i=e[i].nxt) Head[e[i].t]=-1;    int rec_cnt=0,h=0;    Q.h=0;    for (i=1;i<=m;i++){        int x=a[i];        long long sum=0;        for (j=K-1;j>=0 && x>1;j--)            if (sum+dis[x][j]<=t){                sum+=dis[x][j];                x=fa[x][j];            }        if (x<=1) A[rec_cnt]=(poi){Head[top[a[i]]],t-sum},Head[top[a[i]]]=rec_cnt++;        else b[x]=1;    }    for (i=head[1];~i;i=e[i].nxt){        int to=e[i].t,val=e[i].s;        if (!filled(to,1)){            int pos=-1;            f[to]=INF;            for (j=Head[to];~j;j=A[j].nxt)                if (f[to]>A[j].t) pos=j,f[to]=A[j].t;            for (j=Head[to];~j;j=A[j].nxt)                if (j!=pos) Q.ins(A[j].t);            Q1[++h]=(node){to,val};        }        else for (j=Head[to];~j;j=A[j].nxt) Q.ins(A[j].t);     }    sort(Q1+1,Q1+1+h);    for (i=1;i<=h;i++){        int x=Q1[i].id,y=Q1[i].s;        long long t1=f[x],t2=INF;        while (Q.h && Q.a[1]<y) Q.del();        if (Q.h) t2=Q.a[1];        if (t1==INF && t2==INF) return 0;        if (t1>t2) Q.del(),Q.ins(t1);    }    return 1;}int main(){    int i;    memset(head,-1,sizeof(head));    rd(n);    for (i=1;i<n;i++){        int x,y,z;        rd(x),rd(y),rd(z);        add_edge(x,y,z);        add_edge(y,x,z);    }    rd(m);    for (i=1;i<=m;i++) rd(a[i]);    long long l=0,r=init(),ans=-1;    while (l<=r){        long long mid=(l+r)>>1;        if (check(mid)) ans=mid,r=mid-1;        else l=mid+1;    }    printf("%lld\n",ans);    return 0;}

Date:2017/10/23
By CalvinJin