NOIP2015
NOIP2015
嘛嘛……作为一只滚回去高考的高三退役OIer,抱着打酱油的心态回来再参加一次NOIP……
今天刚好有心情,还是补一篇题解吧,权当弥补一下没啥时间给学弟学妹(雾)们讲题的补偿……
P.S. 由于高三了= =所以没A的题就不改了……就写写暴力思路好了
其实之前也做了几场比赛,不过时间关系就没写题解……
不过断了这么久大概已经没人会看到了吧……0.0哇一不小心就1点多了
Day1
magic
题目难度很和谐= =签到题,然而本弱鸡写的太慢了囧,被旁边的高二学弟完虐(大雾)
做法:直接分情况讨论模拟吧……
1 //SX-064 Tunix NOIP2015 day1 T1 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;i++) 8 #define F(i,j,n) for(int i=j;i<=n;++i) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 using namespace std; 11 12 typedef long long LL; 13 const int N=50; 14 15 int a[N][N],n; 16 17 int main(){ 18 freopen("magic.in","r",stdin); 19 freopen("magic.out","w",stdout); 20 scanf("%d",&n); 21 22 a[1][n/2+1]=1; 23 int x=1,y=n/2+1; 24 25 F(i,2,n*n){ 26 if (x==1 && y!=n){ 27 a[n][y+1]=i; 28 x=n; y=y+1; 29 } 30 else if (y==n && x!=1){ 31 a[x-1][1]=i; 32 x=x-1; y=1; 33 } 34 else if (x==1 && y==n){ 35 a[2][n]=i; 36 x=2; 37 } 38 else if (x!=1 && y!=n){ 39 if (a[x-1][y+1]==0){ 40 a[x-1][y+1]=i; 41 x=x-1; y=y+1; 42 }else{ 43 a[x+1][y]=i; 44 x=x+1; 45 } 46 } 47 } 48 F(i,1,n) 49 F(j,1,n) printf("%d%c",a[i][j],j==n ? '\n' : ' '); 50 return 0; 51 }
message
由于每个点只有一条出边,所以是个基环内向树(森林)……等等还是说的具体点吧。
首先考虑一个简单点的情况,我们所有的n个点都是连通的,那么这时肯定有且仅有一个环……然后环之外的点呢?会沿着出边不断向前走啊走直到走到环上,因为所有的点都是向里一直走到环上所以叫基环内向树(从环上往外走的叫外向树)(如果有说错请务必指出QAQ万一误人子弟就不好了)
这个其实随便画张图就可以看出来是这样的……这个名字只是比较形象,听起来高大上实际并不高深
然后随便找个点往前走,如果走到之前走过的点那么就找到环了,更新答案,把所有走过的点标记,标记的意思是下次再走到这个环的时候就可以直接返回了……
这样就是O(n)的dfs即可
考试的最后半小时我一直在蛋疼为什么过不了20W的极限数据,快结束的时候才想起来Windows下栈空间有限制……sad
1 //SX-064 Tunix NOIP2015 day1 T2 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;i++) 8 #define F(i,j,n) for(int i=j;i<=n;i++) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 using namespace std; 11 const int N=200010; 12 13 int to[N]; 14 15 int n,ans,dep[N]; 16 bool vis[N],in[N]; 17 18 void dfs(int x){ 19 vis[x]=1; 20 in[x]=1; 21 if (!vis[to[x]]){ 22 dep[to[x]]=dep[x]+1; 23 dfs(to[x]); 24 }else{ 25 if (in[to[x]]){ 26 ans=min(ans,dep[x]-dep[to[x]]+1); 27 } 28 } 29 in[x]=0; 30 } 31 32 int main(){ 33 freopen("message.in","r",stdin); 34 freopen("message.out","w",stdout); 35 scanf("%d",&n); 36 F(i,1,n) scanf("%d",&to[i]); 37 38 ans=100000000; 39 F(i,1,n) if (!vis[i]) dfs(i); 40 printf("%d\n",ans); 41 return 0; 42 }
landlords
= =感觉超爽的一道题,maya这大概是我写过的唯一一道模拟?搜索?长代码题了……
其实暴力的思路很简单,枚举方案就可以了,然而我们要尽量减少相同状态的重复搜索,比如我先出三带一,再出炸弹,和先出炸弹再出三带一是一样的……这种重复枚举要避免,所以我们按出牌方式为层数深搜,然后……其实各种情况有相似的,Ctrl+C & Ctrl+V大法吼。
考试的时候由于细节问题没找到出错点……花了十几二十分钟捣鼓Windows下怎么搞GDB……
反正最后调的挺爽的……总之整体思路清晰的话做出来不难
然而傻逼的我忘记了加最优性剪枝QAQ,也就是 if (now >= ans) return;
结果最后一个点T掉了……sad
1 //SX-064 Tunix NOIP2015 day1 T3 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;i++) 8 #define F(i,j,n) for(int i=j;i<=n;i++) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 using namespace std; 11 const int N=20; 12 13 int a[15],n;//a[i]表示数码为i的牌有多少张(反正花色无用了……) 14 int tot_yi,yi[20]; 15 int tot_dui,dui[20]; 16 int tot_san,san[20]; 17 int tot_si,si[20]; 18 19 void init(){ 20 memset(a,0,sizeof a); 21 tot_yi = tot_dui = tot_san = tot_si = 0; 22 int x,y; 23 F(i,1,n){ 24 scanf("%d%d",&x,&y); 25 a[x]++; 26 } 27 if (a[0]>=1) yi[++tot_yi]=0; 28 F(i,1,13){//鬼牌单独考虑 29 if (a[i]>=1) yi[++tot_yi]=i; 30 if (a[i]>=2) dui[++tot_dui]=i; 31 if (a[i]>=3) san[++tot_san]=i; 32 if (a[i]==4) si[++tot_si]=i; 33 } 34 } 35 /********************work***********************/ 36 37 int ans; 38 //能出顺子就出,同一种出牌组合只枚举一次(顺+单 和 单+顺 不重复枚举) 39 const int tot=11; 40 41 void dfs(int x,int tmp,int y){//手里有x张牌,当前出了tmp次,出牌方式为第y种 42 if (tmp>=ans) return;//!!!就是这句考试时没加!! 43 if (x==0){ 44 ans=min(ans,tmp); 45 return; 46 } 47 if (y==1){//出单顺子 48 F(len,5,12){ 49 bool can=0; 50 F(i,3,13-len+2){ 51 bool sign=1; 52 F(j,0,len-1){ 53 int t=i+j; 54 if (t==14) t=1; 55 if (a[t]<1){ sign=0; break;} 56 } 57 if (sign){//如果找到这样一组顺子 58 can=1; 59 F(j,0,len-1){ 60 int t=i+j; 61 if (t==14) t=1; 62 a[t]--; 63 } 64 F(z,y,tot) dfs(x-len,tmp+1,z); 65 F(j,0,len-1){ 66 int t=i+j; 67 if (t==14) t=1; 68 a[t]++; 69 } 70 } 71 } 72 if (!can) return;//如果找不到长为len的顺子,那一定没有len+1的顺子,直接return,不继续往后找了 73 }//出单顺子 74 } 75 else if (y==2){//出双顺子 76 F(len,3,12){//枚举顺子的长度 77 bool can=0; 78 F(i,3,13-len+2){//枚举顺子的最小数码 79 bool sign=1; 80 F(j,0,len-1){ 81 int t=i+j; 82 if (t==14) t=1; 83 if (a[t]<2){ sign=0; break;} 84 } 85 if (sign){//如果找到这样一组顺子 86 can=1; 87 F(j,0,len-1){ 88 int t=i+j; 89 if (t==14) t=1; 90 a[t]-=2; 91 } 92 F(z,y,tot) dfs(x-len*2,tmp+1,z); 93 F(j,0,len-1){ 94 int t=i+j; 95 if (t==14) t=1; 96 a[t]+=2; 97 } 98 } 99 } 100 if (!can) return;//如果找不到长为len的顺子,那一定没有len+1的顺子,直接return,不继续往后找了 101 }//出双顺子 102 } 103 else if (y==3){//出三顺子 104 F(len,2,12){//枚举顺子的长度 105 bool can=0; 106 F(i,3,13-len+2){//枚举顺子的最小数码 107 bool sign=1; 108 F(j,0,len-1){ 109 int t=i+j; 110 if (t==14) t=1; 111 if (a[t]<3){ sign=0; break;} 112 } 113 if (sign){//如果找到这样一组顺子 114 can=1; 115 F(j,0,len-1){ 116 int t=i+j; 117 if (t==14) t=1; 118 a[t]-=3; 119 } 120 F(z,y,tot) dfs(x-len*3,tmp+1,z); 121 F(j,0,len-1){ 122 int t=i+j; 123 if (t==14) t=1; 124 a[t]+=3; 125 } 126 } 127 } 128 if (!can) return;//如果找不到长为len的顺子,那一定没有len+1的顺子,直接return,不继续往后找了 129 }//出三顺子 130 } 131 else if (y==4){//出4带2单 132 if (x<6) return;//牌数太少,不可能出4带2单 133 int four=-1; 134 F(i,1,tot_si) if (a[si[i]]==4) four=si[i]; 135 if (four==-1) return;//没有4张的,没法四带2 136 F(i,1,tot_si){//枚举【4】 137 if (a[si[i]]==4){ 138 four=si[i]; 139 a[four]-=4; 140 F(j,1,tot_yi){//枚举第一个单张(可以带鬼牌) 141 if (a[yi[j]]>=1){ 142 a[yi[j]]--; 143 F(k,j,tot_yi){//两个单牌可以一样 144 if (a[yi[k]]>=1){ 145 a[yi[k]]--; 146 147 F(z,y,tot) dfs(x-6,tmp+1,z); 148 149 a[yi[k]]++; 150 } 151 } 152 a[yi[j]]++; 153 } 154 } 155 a[four]+=4; 156 } 157 } 158 } 159 else if (y==5){//出4带2对 160 if (x<8) return;//牌数太少,不可能出4带2对 161 int four=-1; 162 F(i,1,tot_si) if (a[si[i]]==4) four=si[i]; 163 if (four==-1) return;//没有4张的,没法四带2 164 F(i,1,tot_si){//枚举【4】 165 if (a[si[i]]==4){ 166 four=si[i]; 167 a[four]-=4; 168 F(j,1,tot_dui){//枚举第一个对 169 if (a[dui[j]]>=2){ 170 a[dui[j]]-=2; 171 F(k,j+1,tot_dui){ 172 if (a[dui[k]]>=2){ 173 a[dui[k]]-=2; 174 175 F(z,y,tot) dfs(x-8,tmp+1,z); 176 177 a[dui[k]]+=2; 178 } 179 } 180 a[dui[j]]+=2; 181 } 182 } 183 a[four]+=4; 184 } 185 } 186 } 187 else if (y==6){//出三带1 188 if (x<4) return;//牌数太少,不可能出3带1 189 int three=-1; 190 F(i,1,tot_san) if (a[san[i]]>=3) three=san[i]; 191 if (three==-1) return;//没有三张的没法三带一 192 F(i,1,tot_san){ 193 if (a[san[i]]>=3){ 194 three=san[i]; 195 a[three]-=3; 196 F(j,1,tot_yi){ 197 if (a[yi[j]]>=1){ 198 a[yi[j]]--; 199 200 F(z,y,tot) dfs(x-4,tmp+1,z); 201 202 a[yi[j]]++; 203 } 204 } 205 a[three]+=3; 206 } 207 } 208 } 209 else if (y==7){//出三带一对 210 if (x<5) return; //牌数太少,不可能出3带一对 211 int three=-1; 212 F(i,1,tot_san) if (a[san[i]]>=3) three=san[i]; 213 if (three==-1) return;//没有三张的没法三带一对 214 F(i,1,tot_san){ 215 if (a[san[i]]>=3){ 216 three=san[i]; 217 a[three]-=3; 218 F(j,1,tot_dui){ 219 if (a[dui[j]]>=2){ 220 a[dui[j]]-=2; 221 222 F(z,y,tot) dfs(x-5,tmp+1,z); 223 224 a[dui[j]]+=2; 225 } 226 } 227 a[three]+=3; 228 } 229 } 230 } 231 else if (y==8){//出三张 232 if (x<3) return; 233 F(i,1,tot_san) 234 if (a[san[i]]>=3){ 235 a[san[i]]-=3; 236 237 F(z,y,tot) dfs(x-3,tmp+1,z); 238 239 a[san[i]]+=3; 240 } 241 } 242 else if (y==9){//出对子 243 if (x<2) return; 244 F(i,1,tot_dui) 245 if (a[dui[i]]>=2){ 246 a[dui[i]]-=2; 247 248 F(z,y,tot) dfs(x-2,tmp+1,z); 249 250 a[dui[i]]+=2; 251 } 252 } 253 else if (y==10){//出火箭 254 if (a[0]==2){ 255 a[0]=0; 256 F(z,y,tot) dfs(x-2,tmp+1,z); 257 a[0]=2; 258 } 259 } 260 else if (y==11){//出单张 261 dfs(0,tmp+x,12); 262 } 263 } 264 265 void work(){ 266 ans=n; 267 F(i,1,tot) dfs(n,0,i); 268 printf("%d\n",ans); 269 } 270 void work30(){ 271 int ans=n; 272 F(i,0,13){ 273 if (a[i]==2) ans--; 274 if (a[i]==3) ans=1; 275 if (a[i]==4) ans=1; 276 } 277 printf("%d\n",ans); 278 } 279 /********************work***********************/ 280 281 int main(){ 282 freopen("landlords.in","r",stdin); 283 freopen("landlords.out","w",stdout); 284 int T; 285 scanf("%d%d",&T,&n); 286 while(T--){ 287 init(); 288 if (n<=4) work30(); 289 else work(); 290 } 291 return 0; 292 }
Day2
stone
二分答案,O(n)判定,也就是我们二分一个石子间最小距离,然后从第一个开始往出跳……如果距离太近就删掉这个石子,如果足够远就跳过去,继续跳……
然而最后一个石子的时候(咦好像应该是石头?)如果离终点太近,其实是应该再删掉最后一个石子,然后再判断是否超过m的……而我直接返回0了QAQ
最后十分钟一定不要轻易改程序啊……QAQ
不过幸好出题人好心没有卡我=w=
1 //SX-064 Tunix NOIP2015 day2 T1 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;i++) 8 #define F(i,j,n) for(int i=j;i<=n;i++) 9 #define D(i,j,n) for(int i=j;i>=n;i--) 10 using namespace std; 11 12 const int N=50000; 13 14 int l,n,m,d[N]; 15 16 bool check(int len){ 17 int pos=0,now=0; 18 F(i,1,n){ 19 if (d[i]-d[pos]<len){ 20 now++; 21 if (i==n) return 0; 22 if (now>m) return 0; 23 } 24 else pos=i; 25 } 26 return 1; 27 } 28 29 int main(){ 30 freopen("stone.in","r",stdin); 31 freopen("stone.out","w",stdout); 32 33 scanf("%d%d%d",&l,&n,&m); 34 F(i,1,n) scanf("%d",&d[i]); 35 36 d[n+1]=l; 37 n++; 38 39 int L=1,R=l,mid=(L+R)/2,ans=0; 40 while(L<=R){ 41 mid=(L+R)>>1; 42 if (check(mid)) ans=mid,L=mid+1; 43 else R=mid-1; 44 } 45 printf("%d\n",ans); 46 return 0; 47 }
substring
感觉很棒的一道DP
一开始根本不会做怎么办!老师教导我们:要先从简单问题入手!从特殊到一般!
首先我们先考虑k=m的情况,这时候我们只需要从A串中找出一个子序列等于B串就可以了。那么我们可以设计出如下DP:
f[i][j]表示A串的前 i 个字符中选出 j 个与B串的前 j 位匹配,且使用了A串的第 i 个字符
那么转移就很简单了:如果a[i]==b[j]那么 $f[i][j]=\sum f[l][j-1] (a[l]==b[j-1])$也就是每次考虑一个字母……如果a[i]!=b[j] 那么f[i][j]=0
然后我们考虑加入只选 k 个子串这个限制:
f[i][j][k]表示从A串的前 i 个字符中选出 j 个与B串的前 j 位匹配,且使用了A串的第 i 个字符与B[j]匹配
转移时只需要额外考虑一下:转移过来的那个状态末位是否与第 i 个字符相连(也就是分两种情况讨论一下再加起来)
如果a[i]==b[j]
1.与之前的情况相同,从k-1层转移到第k层,方案数加起来(复杂度$O(n)$)
2.末位相连,也就是从第k层转移到第k层。此时如果a[i-1]==b[j-1] 那么 f[i][j][k]+=f[i-1][j-1][k](加长第k个子串,也就是末位相连)
这时我们就有了一个时间复杂度为 $O(n^2 * m*k) = O(n^2m^2)$,空间复杂度为$O(nm^2)$的算法了。那么这明显是要爆的……怎么优化呢?
空间上我们容易发现,第k层只与第k-1层和第k层有关,所以我们可以利用滚动数组。
时间上我们发现,第一种转移的所有方案数其实可以前缀和优化!也就是令g[i][j][k]表示从A串的前 i 个字符中选出 j 个与B串的前 j 位匹配,且不要求一定使用A串的第 i 个字符,那么转移就变成了。
1.f[i][j][k]=g[i-1][j-1][k-1]
2.与之前一样
g[i][j][k]=g[i-1][j][k]+f[i][j][k]
至此我们这道题就完满解决了= =
1 //SX-064 Tunix NOIP2015 day2 T2 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;i++) 8 #define F(i,j,n) for(int i=j;i<=n;i++) 9 #define D(i,j,n) for(int i=j;i>=n;i--) 10 using namespace std; 11 12 const int P=1000000007; 13 int n,m,k; 14 //int a[1010][210][210];//爆空间了…… 15 int f[2][1010][210];//f[k][i][j]前i个字符中取出k个子串匹配了前m个字符 16 //且使用了第i个字符 17 int g[2][1010][210];//g[k][i][j]前i个字符中取出k个子串匹配了前m个字符 18 19 char s1[1010],s2[1010]; 20 21 int main(){ 22 freopen("substring.in","r",stdin); 23 freopen("substring.out","w",stdout); 24 scanf("%d%d%d",&n,&m,&k); 25 scanf("%s",s1+1); 26 scanf("%s",s2+1); 27 28 f[0][0][0]=1; 29 F(i,0,n) g[0][i][0]=1; 30 /* F(t,1,k){ 31 int now=t&1; 32 memset(f[now],0,sizeof f[now]); 33 F(i,1,n) F(j,1,min(i,m)) if (s1[i]==s2[j]){ 34 F(l,0,i-1) 35 if (s1[l]==s2[j-1]) (f[now][i][j]+=f[now^1][l][j-1])%=P; 36 if (s1[i-1]==s2[j-1]) (f[now][i][j]+=f[now][i-1][j-1])%=P; 37 } 38 }//把转移单调队列优化一下? 39 */ 40 F(t,1,k){ 41 int now=t&1; 42 memset(f[now],0,sizeof f[now]); 43 memset(g[now],0,sizeof g[now]); 44 F(i,1,n) F(j,1,min(i,m)){ 45 if (s1[i]==s2[j]){ 46 (f[now][i][j]+=g[now^1][i-1][j-1])%=P; 47 if (s1[i-1]==s2[j-1]) (f[now][i][j]+=f[now][i-1][j-1])%=P; 48 } 49 g[now][i][j]=(g[now][i-1][j]+f[now][i][j])%P; 50 } 51 }//把转移单调队列优化一下? 并不是= =前缀和优化一下就可以了 52 printf("%d\n",g[k&1][n][m]); 53 return 0; 54 } 55 /* 56 2147483647 57 1000000007 58 2000000014 59 */
transport
我比较弱并没想出正解QwQ时间也比较紧张(因为day2的第二题耗费的时间有点久了)只打了60分的暴力(实际上只有55分)
其中20分是只有一条路径……那么只需要找出最长的那个删去即可
40分是一条链的情况,我的做法是:
每条航线都可以看作是一条区间,我们将这些区间按照左端点排序,那么我们发现:【被其他大区间包含的小区间在去掉某一段以后也不可能成为最长】,那么我们将这种情况处理一下,就得到了一个【左端点单调递增,且右端点单调递增】的区间序列,然后我们可以枚举删掉的那一条航道,它影响到的区间必然是【连续的一组区间】,那么我们用两个单调的左右指针确定一下当前航道会影响到的区间是连续的哪一段,用线段树计算一下这一段减去a[i]后,全局的最大值,然后再加回来这个a[i]……就可以$O(nlogn)$得出答案了
我最后只得了55分……是有一个n=100,m=100,且整个是一条链的数据点挂掉了……也不知道是为什么……QwQ
1 //SX-064 Tunix NOIP2015 day2 T3 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;i++) 8 #define F(i,j,n) for(int i=j;i<=n;i++) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 using namespace std; 11 12 int getint(){ 13 int r=1,v=0; char ch=getchar(); 14 for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1; 15 for(; isdigit(ch);ch=getchar()) v=(v<<3)+(v<<1)-'0'+ch; 16 return r*v; 17 } 18 const int N=300010,INF=1000000000; 19 /*******************template********************/ 20 21 struct seg{ 22 int x,y,l; 23 bool operator < (const seg &b) const { return (x<b.x || (x==b.x && y>b.y)); } 24 }a[N],b[N]; 25 int n,m,x[N],y[N],d[N],s[N]; 26 27 int ans; 28 29 int t[N<<2],tag[N]; 30 #define mid (l+r>>1) 31 #define L (o<<1) 32 #define R (o<<1|1) 33 #define lch L,l,mid 34 #define rch R,mid+1,r 35 void Push_down(int o){ 36 if (tag[o]){ 37 tag[L]+=tag[o]; t[L]+=tag[o]; 38 tag[R]+=tag[o]; t[R]+=tag[o]; 39 tag[o]=0; 40 } 41 } 42 void maintain(int o){ 43 t[o]=max(t[L],t[R]); 44 } 45 46 void build(int o,int l,int r){ 47 if (l==r) t[o]=b[l].l; 48 else{ 49 build(lch); 50 build(rch); 51 maintain(o); 52 } 53 } 54 int ql,qr; 55 void update(int o,int l,int r,int v){ 56 if (ql<=l && qr>=r) t[o]+=v,tag[o]+=v; 57 else{ 58 Push_down(o); 59 if (ql<=mid) update(lch,v); 60 if (qr>mid) update(rch,v); 61 maintain(o); 62 } 63 } 64 65 66 void work(){ 67 sort(a+1,a+m+1); 68 // F(i,1,m) printf("%d %d\n",a[i].x,a[i].y); 69 int now=0; 70 F(i,1,m){ 71 if (a[i].x > b[now].x && a[i].y > b[now].y) b[++now]=a[i]; 72 } 73 /* 74 F(i,1,now) 75 printf("%d %d\n",b[i].x,b[i].y); 76 */ 77 F(i,1,now) 78 b[i].l=s[b[i].y]-s[b[i].x]; 79 build(1,1,now); 80 int nowl=1,nowr=1; 81 ans=1000000000; 82 F(i,1,n){ 83 if (b[nowl].x>=i) continue; 84 if (nowr<nowl) nowr=nowl; 85 while(b[nowr+1].x<i) nowr++; 86 87 ql=nowl; qr=nowr; 88 update(1,1,now,-d[i]); 89 ans=min(ans,t[1]); 90 update(1,1,now,d[i]); 91 } 92 printf("%d\n",ans); 93 } 94 95 96 int to[N<<1],nxt[N<<1],head[N],cnt,len[N<<1]; 97 void add(int x,int y,int z){ 98 to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; len[cnt]=z; 99 to[++cnt]=x; nxt[cnt]=head[y]; head[y]=cnt; len[cnt]=z; 100 } 101 102 int dist[N],mx[N],Q[N]; 103 bool inq[N]; 104 void pianfen(){ 105 F(i,2,n){ 106 int x=getint(),y=getint(),z=getint(); 107 add(x,y,z); 108 } 109 int S=getint(),T=getint(); 110 F(i,0,n) dist[i]=INF; 111 int l=0,r=0; 112 Q[0]=S; inq[S]=1; dist[S]=0; 113 while(l<=r){ 114 int x=Q[l++]; 115 inq[x]=0; 116 for(int i=head[x];i;i=nxt[i]) 117 if (dist[to[i]]>dist[x]+len[i]){ 118 dist[to[i]]=dist[x]+len[i]; 119 mx[to[i]]=max(len[i],mx[x]); 120 if (!inq[to[i]]){ 121 Q[++r]=to[i]; 122 inq[to[i]]=1; 123 } 124 } 125 } 126 printf("%d\n",dist[T]-mx[T]); 127 } 128 129 int main(){ 130 freopen("transport.in","r",stdin); 131 freopen("transport.out","w",stdout); 132 n=getint(); m=getint(); 133 if (m==1){ 134 pianfen(); 135 return 0; 136 } 137 F(i,2,n){ 138 int x=getint(),y=getint(),l=getint(); 139 if (x>y) swap(x,y); 140 d[y]=l; 141 } 142 F(i,2,n) s[i]=s[i-1]+d[i];//链状情况偏分 143 F(i,1,m){ 144 a[i].x=getint(); a[i].y=getint(); 145 if (a[i].x>a[i].y) swap(a[i].x,a[i].y); 146 } 147 work(); 148 return 0; 149 }
最后的话:
总之感觉这次的题目day2的思维量比day1还是要高一些的,相应的题目质量也要整体高一些。而且感觉上Day2 T2这个DP的难度应该已经比去年的D1T3 Flappy Bird要难了吧……
一些小地方还是失误了……看来时间一长就容易生疏……
希望后继的OIer们能够再接再厉>_<也祝当年高一的友人NOI2016 RP++! @lct1999
OI生涯中最后一场比赛,也算是为我的OI生涯画上了一个句号了。