2016.7.22.noip2012D2
mod:
扩展欧几里德算法,而我没有复习,暴力的60
事后后悔死
classroom:
线段树得90,T两个点。代码与问题如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 using namespace std; 5 int n,m,s,t; 6 long long r[1000005],d; 7 struct stu 8 { 9 int l,r; 10 long long mi,val; 11 }tree[4000005]; 12 int minn(int q1,int q2) 13 { 14 if(q1>q2) return q2; 15 return q1; 16 } 17 void pushdown(int v) 18 { 19 if(tree[v].l==tree[v].r) return ; 20 tree[v<<1].val+=tree[v].val; 21 tree[(v<<1)+1].val+=tree[v].val; 22 tree[v<<1].mi-=tree[v].val;//-=忘了,-的只有本次的值也忘了 23 tree[(v<<1)+1].mi-=tree[v].val; 24 tree[v].val=0; 25 } 26 void build(int left,int right,int u) 27 { 28 tree[u].l=left; 29 tree[u].r=right; 30 if(left==right) 31 { 32 tree[u].mi=r[left]; 33 return ; 34 } 35 int mid=(left+right)>>1; 36 build(left,mid,u<<1); 37 build(mid+1,right,(u<<1)+1); 38 tree[u].mi=minn(tree[u<<1].mi,tree[(u<<1)+1].mi); 39 } 40 bool query(int u,int left,int right,long long dd) 41 { 42 bool flag; 43 if(tree[u].l==left&&tree[u].r==right) 44 { 45 46 if(tree[u].mi>=dd) 47 { 48 tree[u].mi-=dd; 49 tree[u].val+=dd; 50 return true; 51 } 52 return false; 53 } 54 if(tree[u].val) pushdown(u); 55 int mid=(tree[u].l+tree[u].r)>>1; 56 if(right<=mid) flag=query(u<<1,left,right,dd); 57 else if(left>mid) flag=query((u<<1)+1,left,right,dd); 58 else if(query(u<<1,left,mid,dd)&&query((u<<1)+1,mid+1,right,dd)) flag=true; 59 else flag=false; 60 tree[u].mi=minn(tree[u<<1].mi,tree[(u<<1)+1].mi);//向上更新,在合并的时候忘了 61 return flag; 62 } 63 int main() 64 { 65 freopen("classroom.in","r",stdin); 66 freopen("classroom.out","w",stdout); 67 scanf("%d %d",&n,&m); 68 for(int i=1;i<=n;i++) 69 scanf("%lld",&r[i]); 70 build(1,n,1); 71 for(int j=1;j<=m;j++) 72 { 73 scanf("%lld %d %d",&d,&s,&t); 74 if(query(1,s,t,d)==false) 75 { 76 printf("-1\n"); 77 printf("%d\n",j); 78 return 0; 79 } 80 } 81 printf("0\n"); 82 return 0; 83 }
另,正解是二分订单,如下:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; int n,m,s[1000005],t[1000005],ans; int r[1000005],d[1000005],a[1000005]; void depart(int l,int ri) { memset(a,0,sizeof(a)); bool flag=false; if(l==ri) { return ; } int midd=(l+ri)>>1; int sum=0; for(int i=1;i<=midd;i++) { a[s[i]]+=d[i]; a[t[i]+1]-=d[i]; } for(int i=1;i<=n;i++) { sum+=a[i]; if(sum>r[i]) { flag=true; break; } } if(flag){ ans = midd; depart(l,midd); }else depart(midd+1,ri); } int main() { freopen("classroom.in","r",stdin); freopen("classroom.out","w",stdout); scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&r[i]); for(int j=1;j<=m;j++) scanf("%d %d %d",&d[j],&s[j],&t[j]); depart(1,m); if(ans) { printf("-1\n"); printf("%d\n",ans); return 0; } printf("0\n"); return 0; }
blockade:
惊呆,看标答都看了1小时...借大神的程序配大神的注释与自己的理解。
#include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #define INF 999999999999 #define LL long long #define Max(x,y) if(x<y) x=y; #define N 100003 using namespace std ; struct P{ LL p,t; P():p(),t(){} P(const LL x,const LL y) : p(x),t(y){} }army[N],edge[N]; bool cmp(const P &x,const P &y){ return x.t>y.t; } LL n,m,T; LL next[N*2],last[N*2],to[N*2]; LL w[N*2]; LL p[N],from[N],root[N]; LL dis[N],left[N]; bool use[N]; queue<LL> q; inline void addedge(LL a,LL b,LL c){//存边的关系 next[++T]=last[a]; last[a]=T; to[T]=b ; w[T]=c; next[++T]=last[b]; last[b]=T; to[T]=a ; w[T]=c; } void fail(){//比较军队数和根节点连接的路径数,说白了特判 LL Tot=0; for(LL i=last[1];i;i=next[i]) ++Tot; if(Tot>m) { printf("-1"); exit(0); } } LL pushup(LL now,LL f){//军队移到节点now(now父点是f)剩余的时间。 if(!next[last[now]]) {//叶子结点 if(left[now]>=0ll) use[now]=1 ;//守住now结点 return left[now] ; } use[now]=1;//不赋值,有1组数据错;假设可以守住now结点 for(LL i=last[now];i;i=next[i]) if(to[i]!=f){//不是父亲节点 Max(left[now],pushup(to[i],now)-w[i]);//now的时间选left[now]与下面结点剩的时间中的最大值 if(!use[to[i]]) use[now]=0;//now的子结点没守住,则now也可能没守住 } if(left[now]>=0) use[now]=1;//军队到now结点所剩时间>=0,能管住节点now return left[now]; } bool check(LL lim){ LL numa=0,numb=0; memset(left,-1,sizeof left);//剩余时间 memset(use,0,sizeof use);//是否经过 memset(from,0,sizeof from);//从哪里来 for(LL i=1;i<=m;i++) if(dis[p[i]]>=lim) left[p[i]]=lim;//走不到根1结点,该军队的时限 else army[++numa]=P(root[p[i]],lim-dis[p[i]]);//从根1下的root[p[i]]来,生活剩lim-dis[p[i]]时间 pushup(1,0); for(LL i=last[1];i;i=next[i]) if(!use[to[i]]) edge[++numb]=P(to[i],w[i]); if(numb>numa) return false; // 军队不够 sort(army+1,army+numa+1,cmp); sort(edge+1,edge+numb+1,cmp); //----来自该节点的所剩时间最少的军队---- for(LL i=numa;i;i--) if(!from[army[i].p]) from[army[i].p]=i; //-------------------------------------- LL la=1,lb=1; memset(use,0,sizeof use); //归并,从大到小,有一个不满足就全不满足 use[0]=1; while(lb<=numb&&la<=numa){ if(use[la]){ ++la; continue ; } if(!use[from[edge[lb].p]]){ use[from[edge[lb].p]]=1; ++lb; continue ; } if(army[la].t<edge[lb].t) return false ; use[la]=1; ++la ; ++lb ; } return true ; } void prework(){ q.push(1);//队列 use[1]=1; for(LL i=last[1];i;i=next[i]) root[to[i]]=to[i];//与1相连的所有点 ,父亲为自己 while(!q.empty()){//队列非空 LL now=q.front(); q.pop(); for(LL i=last[now];i;i=next[i]) if(!use[to[i]]){ if(now!=1) root[to[i]]=root[now];//记爸爸 dis[to[i]]=dis[now]+w[i];//深度 use[to[i]]=1;//走没走 q.push(to[i]); } } } int main(){ freopen("blockade.in","r",stdin); freopen("blockade.out","w",stdout); scanf("%I64d",&n); LL a,b,c; for(LL i=1;i<n;i++){ scanf("%I64d%I64d%I64d",&a,&b,&c); addedge(a,b,c); } scanf("%I64d",&m); fail(); for(LL i=1;i<=m;i++) scanf("%I64d",&p[i]); LL ans,l=0,r=100000000000000LL;//时间范围,尽量取大 prework();//广搜预处理 while(l<=r){//二分时间 LL mid=(l+r)>>1; if(check(mid)){ ans=mid; r=mid-1; } else l=mid+1; } printf("%I64d",ans); return 0; }