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 }
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 }
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 }