NOIp 2012 Day2 解题报告

1.   同余方程

定理:不定方程ax+by=c当且仅当(a,b)|c时有解。设(a,b)=d,若x=x0, y=y0为一组解,则x=x0+kb/d, y=y0-ka/d(k∈Z)也是一组解,且是全部解。

分析题目知,答案为不定方程ax+my=1的最小x解。由于数据保证有解,则(a,m)|1,也就是说(a,b)=1,所以用扩展欧几里得求出一组解,在将它加或减若干b得到最小整数解。

复杂度O(logA)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 
 8 //variable//
 9 int a,b,x,y;
10 
11 //function prototype//
12 int exgcd(int,int,int&,int&);
13 
14 //solve//
15 int main(){
16     scanf("%d%d",&a,&b);
17     exgcd(a,b,x,y);
18     while (x>b) x-=b;
19     while (x<0) x+=b;
20     printf("%d\n",x);
21     return 0;
22 }
23 
24 int exgcd(int a,int b,int &x,int &y){
25     if (!b){
26         x=1,y=0;
27         return a;
28     }
29     int d=exgcd(b,a%b,x,y);
30     int t=x;
31     x=y;
32     y=t-a/b*y;
33     return d;
34 }
View Code

2.   借教室

用线段树的话会TLE,能得70分。

我们设delta[i]代表若干操作后i减少的教室数。则delta的求法是:对于每个dj, sj, tj,令delta[sj]=delta[sj]−dj, delta[tj+1]=delta[tj+1]+dj,做完所有j后对delta求前缀和。

二分答案m,对1~m的租借订单求delta。若在1~n内有任意一个i使得r[i]+delta[i]<0,则r=m−1,否则l=m+1。二分结束之后的l就是答案。

复杂度O(NlogM)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 
 8 //variable//
 9 int n,m,r[1000010],d[1000010],s[1000010],t[1000010];
10 int delta[1000010];
11 char input[10000000];
12 
13 //function prototype//
14 void read_data(void);
15 
16 //solve//
17 int main(){
18     read_data();
19     int left=1,right=m;
20     while (left<=right){
21         int mid=left+right>>1;
22         bool flag=true;
23         memset(delta,0,sizeof delta);
24         for (int i=1;i<=mid;++i){
25             delta[s[i]]-=d[i];
26             delta[t[i]+1]+=d[i];
27         }
28         for (int i=1;i<=n;++i){
29             delta[i]+=delta[i-1];
30         }
31         for (int i=1;i<=n;++i){
32             if (r[i]+delta[i]<0){
33                 flag=false;
34                 break;
35             }
36         }
37         if (flag){
38             left=mid+1;
39         }else{
40             right=mid-1;
41         }
42     }
43     if (left>m){
44         puts("0");
45     }else{
46         printf("-1\n%d\n",left);
47     }
48     return 0;
49 }
50 
51 void read_data(){
52     gets(input);char *pos=input;
53     n=strtol(pos,&pos,10);
54     m=strtol(pos,&pos,10);
55     gets(input);pos=input;
56     for (int i=1;i<=n;++i){
57         r[i]=strtol(pos,&pos,10);
58     }
59     for (int i=1;i<=m;++i){
60         gets(input);pos=input;
61         d[i]=strtol(pos,&pos,10);
62         s[i]=strtol(pos,&pos,10);
63         t[i]=strtol(pos,&pos,10);
64     }
65 }
View Code

3.   疫情控制

神级树上倍增题。由于所有军队可以同时移动,且时间(即路程)有前缀和性质,于是想到倍增。

设ance[i][j]代表点i的2的j次方倍祖先,sum[i][j]代表i到它的2的j次方倍祖先之间的路径和。

不容易直接求出答案,于是二分答案。对于二分到的时间t,判定它是否可行。

对于给定时间t,让每个军队最大限度向根走,倍增求它最高能到哪里,如果没有走到根,停下来,标记它所在的点flag[x]=1;如果走到根,记录它来自根的哪颗子树,以及还有多少剩余时间可走。

然后推flag数组:如果一个点的父亲的flag为1,则它的flag为1。如果一个非叶子节点的所有儿子的flag都为1,则它的flag为1。用DFS实现。

求两个列表:1的flag非1的儿子的列表(存储它的编号和它到1的距离)和倍增到1的军队列表(存储它来自哪个以1的儿子为根的子树,以及它剩余的时间)。

下面是用贪心性质:如果一个到了1的军队,其剩余时间不足以让它回到它来的那个1的儿子,且这个儿子的flag=0,则将它退回去,对应儿子的flag变成1,把这个军队和对应儿子分别在两个列表里删除。

将两个列表按照时间从大到小排序,然后小对小一一匹配,军队能过去就将flag置为1,否则flag保持为0。

最后看1的儿子是否flag都为1,是,则t可行,否则不可行。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<climits>
  6 #include<algorithm>
  7 using namespace std;
  8 
  9 //type//
 10 struct rec{
 11     int x;
 12     long long len;
 13 };
 14 
 15 //variable//
 16 int last[50010],prel[100010],dest[100010],cost[100010],tot=1;
 17 int n,m,ance[50010][17],son[50010],loc[50010],from[50010];
 18 long long sum[50010][17];
 19 rec son_list[50010];int son_tot=0;
 20 
 21 int q[50010],vis[50010],flag[50010];
 22 long long rest[50010];
 23 rec to1_list[50010];int to1_tot=0;
 24 
 25 //function prototype//
 26 void addline(int,int,int);
 27 void bfs(int);
 28 void pre(void);
 29 void calc_ans(void);
 30 bool judge(long long);
 31 bool comp(rec,rec);
 32 bool push_flag(int);
 33 
 34 //solve//
 35 int main(){
 36     scanf("%d",&n);
 37     int u,v,w;
 38     for (int i=1;i<n;++i){
 39         scanf("%d%d%d",&u,&v,&w);
 40         addline(u,v,w);
 41         addline(v,u,w);
 42     }
 43     scanf("%d",&m);
 44     for (int i=1;i<=m;++i){
 45         scanf("%d",loc+i);
 46     }
 47     bfs(1);
 48     calc_ans();
 49     return 0;
 50 }
 51 
 52 void bfs(int x){
 53     int he=1,ta=1;
 54     q[1]=x;vis[x]=1;
 55     while (he<=ta){
 56         int u=q[he++];
 57         for (int k=last[u];k;k=prel[k]){
 58             if (!vis[dest[k]]){
 59                 vis[dest[k]]=1;
 60                 q[++ta]=dest[k];
 61                 ++son[u];
 62                 ance[dest[k]][0]=u;
 63                 sum[dest[k]][0]=(long long)cost[k];
 64                 if (u==1){
 65                     from[dest[k]]=dest[k];
 66                 }else{
 67                     from[dest[k]]=from[u];
 68                 }
 69             }
 70         }
 71     }
 72 }
 73 
 74 void pre(){
 75     for (int j=1;j<17;++j){
 76         for (int i=1;i<=n;++i){
 77             ance[i][j]=ance[ance[i][j-1]][j-1];
 78             sum[i][j]=(long long)sum[i][j-1]+sum[ance[i][j-1]][j-1];
 79         }
 80     }
 81 }
 82 
 83 void calc_ans(){    
 84     if (m<son[1]){
 85         puts("-1");
 86         return;
 87     }
 88     pre();
 89     long long l=1ll,r=100000000000000ll;
 90     while (l<=r){
 91         long long mid=((long long)l+r)>>1;
 92         if (judge(mid)){
 93             r=mid-1ll;
 94         }else{
 95             l=mid+1ll;
 96         }
 97     }
 98     cout<<l<<endl;
 99 }
100 
101 bool judge(long long t){
102     memset(flag,0,sizeof flag);
103     son_tot=to1_tot=0;
104     for (int i=1;i<=m;++i){
105         int u=loc[i];
106         long long len=0ll;
107         for (int j=16;j>=0;--j){
108             if (ance[u][j]&&len+sum[u][j]<=t){
109                 len+=sum[u][j];
110                 u=ance[u][j];
111             }
112         }
113         if (u==1){
114             to1_list[++to1_tot]=(rec){from[loc[i]],(long long)t-len};
115         }else{
116             flag[u]=1;
117         }
118     }
119     memset(vis,0,sizeof vis);
120     push_flag(1);
121     if (flag[1]){
122         return true;
123     }
124     for (int i=1;i<=to1_tot;++i){
125         if (!flag[to1_list[i].x]&&to1_list[i].len<sum[to1_list[i].x][0]){
126             flag[to1_list[i].x]=1;
127             swap(to1_list[i],to1_list[to1_tot]);
128             --to1_tot;
129         }
130     }
131     for (int k=last[1];k;k=prel[k]){
132         if (!flag[dest[k]]){
133             son_list[++son_tot]=(rec){dest[k],(long long)cost[k]};
134         }
135     }
136     sort(to1_list+1,to1_list+to1_tot+1,comp);
137     sort(son_list+1,son_list+son_tot+1,comp);
138     for (int i=1,j=1;i<=to1_tot;++i){
139         if (flag[son_list[j].x]) ++j;
140         if (j>son_tot) break;
141         if (to1_list[i].len<son_list[j].len) continue;
142         flag[son_list[j++].x]=1;
143     }
144     bool f=true;
145     for (int k=last[1];k;k=prel[k]){
146         f=(f&&flag[dest[k]]);
147     }
148     if (f&&son[1]){
149         flag[1]=1;
150     }
151     return flag[1];
152 }
153 
154 bool push_flag(int x){
155     vis[x]=1;
156     if (flag[ance[x][0]]){
157         flag[x]=1;
158     }
159     bool f=true;
160     for (int k=last[x];k;k=prel[k]){
161         if (!vis[dest[k]]){
162             bool ff=push_flag(dest[k]);
163             f=(f&&ff);
164         }
165     }
166     if (f&&son[x]){
167         flag[x]=1;
168     }
169     return flag[x];
170 }
171 
172 void addline(int u,int v,int w){
173     dest[++tot]=v;
174     prel[tot]=last[u];
175     last[u]=tot;
176     cost[tot]=w;
177 }
178 
179 bool comp(rec a,rec b){
180     return a.len<b.len;
181 }
View Code

 

posted @ 2015-09-18 16:28  DeAR3327  阅读(212)  评论(0编辑  收藏  举报