• 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 ? '
    ' : ' ');
    50     return 0;
    51 }
    View Code

    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
    ",ans);   
    41     return 0;
    42 } 
    View Code

    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
    ",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
    ",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 }
    View Code

    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
    ",ans);
    46     return 0;
    47 } 
    View Code(好像是有错的吧……)

    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
    ",g[k&1][n][m]);
    53     return 0;
    54 }
    55 /*
    56 2147483647
    57 1000000007
    58 2000000014
    59 */
    View Code

    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
    ",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
    ",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
    ",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
    ",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 }
    View Code

    最后的话:

      总之感觉这次的题目day2的思维量比day1还是要高一些的,相应的题目质量也要整体高一些。而且感觉上Day2 T2这个DP的难度应该已经比去年的D1T3 Flappy Bird要难了吧……

      一些小地方还是失误了……看来时间一长就容易生疏……

      希望后继的OIer们能够再接再厉>_<也祝当年高一的友人NOI2016 RP++! @lct1999

      OI生涯中最后一场比赛,也算是为我的OI生涯画上了一个句号了。

  • 相关阅读:
    [转帖]译文:如何使用SocketAsyncEventArgs类(How to use the SocketAsyncEventArgs class)
    如何建立一个“绑定友好的”usercontrol--wpf
    安卓学习(三)
    安卓学习(二)
    Android学习1
    用伪代码梳理springboot
    用伪代码梳理javaweb从零开始
    用伪代码梳理spring源码
    java如何写出简洁代码
    JAVA修复微信官方SDK支付XXE漏洞
  • 原文地址:https://www.cnblogs.com/Tunix/p/4973353.html
Copyright © 2020-2023  润新知