• NOIp 2015真题模拟赛 By cellur925


    果然我还是最菜的==不接受反驳==

    Day1

    T1:神奇的幻方

    思路:直接模拟即可,由于当前放法只与上一放法有关系,用两个变量记录一下即可。10分钟内切掉==

    预计得分:100分

    实际得分:100分

     1 #include<cstdio>
     2 #include<algorithm>
     3 
     4 using namespace std;
     5 
     6 int n,lx,ly;
     7 int vis[100][100],a[100][100];
     8 
     9 int main()
    10 {
    11     freopen("magic.in","r",stdin);
    12     freopen("magic.out","w",stdout);
    13     scanf("%d",&n);
    14     a[1][(n+1)>>1]=1;
    15     vis[1][(n+1)>>1]=1;
    16     lx=1;ly=(n+1)>>1;
    17     for(int i=2;i<=n*n;i++)
    18     {
    19         if(lx==1&&ly!=n)
    20         {
    21             a[n][ly+1]=i;
    22             vis[n][ly+1]=1;
    23             lx=n,ly++;
    24         }
    25         else if(lx!=1&&ly==n)
    26         {
    27             a[lx-1][1]=i;
    28             vis[lx-1][1]=1;
    29             lx--;ly=1;
    30         }
    31         else if(lx==1&&ly==n)
    32         {
    33             a[lx+1][ly]=i;
    34             vis[lx+1][ly]=1;
    35             lx++;
    36         }
    37         else if(lx!=1&&ly!=n)
    38         {
    39             if(!vis[lx-1][ly+1])
    40             {
    41                 a[lx-1][ly+1]=i;
    42                 vis[lx-1][ly+1]=1;
    43                 lx--;ly++;
    44             }
    45             else
    46             {
    47                 a[lx+1][ly]=i;
    48                 vis[lx+1][ly]=i;
    49                 lx++;
    50             }
    51         }
    52     }
    53     for(int i=1;i<=n;i++)
    54     {
    55         for(int j=1;j<=n;j++)
    56             printf("%d ",a[i][j]);
    57         printf("
    ");
    58     }
    59     return 0;
    60 }
    magic

    T2:信息传递

    思路:方法有多种。可以用tarjan求最小环,或者用拓扑排序,或者用并查集。以前做过,还写了题解 。感觉这是noip第二题少有的简单题了吧。考场上写的tarjan。

    预计得分:100分

    实际得分:80分

    (是这样的qwq 因为实在win下评测的栈小,而且用的cena栈会更小,于是就出锅了qwq,拿到洛咕上是可以满分的,而且这个栈貌似还卡了递归版拓扑排序)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<stack>
     4 #define maxn 200090
     5 
     6 using namespace std;
     7 
     8 int n,r,tot,dfs_clock,scc_cnt,ans=19260817;
     9 int head[maxn],dfn[maxn],low[maxn],size[maxn],scc[maxn];
    10 struct node{
    11     int to,next;
    12 }edge[maxn];
    13 stack<int>s;
    14 
    15 void add(int x,int y)
    16 {
    17     edge[++tot].to=y;
    18     edge[tot].next=head[x];
    19     head[x]=tot;
    20 }
    21 
    22 void tarjan(int x)
    23 {
    24     dfn[x]=low[x]=++dfs_clock;
    25     s.push(x);
    26     for(int i=head[x];i;i=edge[i].next)
    27     {
    28         int y=edge[i].to;
    29         if(!dfn[y])
    30         {
    31             tarjan(y);
    32             dfn[x]=min(dfn[x],dfn[y]);
    33         }
    34         else if(!scc[y]) dfn[x]=min(dfn[x],low[y]);
    35     }
    36     if(low[x]==dfn[x])
    37     {
    38         scc_cnt++;
    39         while(1)
    40         {
    41             int u=s.top();
    42             s.pop();
    43             scc[u]=scc_cnt;
    44             if(x==u) break;
    45         }
    46     }
    47 }
    48 
    49 int main()
    50 {
    51     freopen("message.in","r",stdin);
    52     freopen("message.out","w",stdout);
    53     scanf("%d",&n);
    54     for(int i=1;i<=n;i++)
    55         scanf("%d",&r),add(i,r);
    56     for(int i=1;i<=n;i++)
    57          if(!dfn[i]) tarjan(i);
    58     for(int i=1;i<=n;i++)
    59         size[scc[i]]++;
    60     for(int i=1;i<=scc_cnt;i++)
    61         if(size[i]!=1&&size[i]<ans) ans=size[i];
    62     printf("%d",ans);
    63     return 0;
    64 }
    message

    T3:斗地主

    思路:我没玩过斗地主qwq,表示看到题理解游戏规则就用了好久。这貌似是个搜索,不过noip还会考裸的搜索嘛qwq(不过真的考了)。平时搜索能力就很弱,独立写出搜索的情况很少,不过最近有意在练习搜索了qwq,也已经有了自己的理解,对搜索应该还是缺乏训练。这篇写了题解(在这里)。考场上写了n=2/3/4的子任务,就是骗分了。

    预计得分:30分

    实际得分:20分

    (是这样的qwq,n==4的情况我应该是写判断的时候出锅了,暴力都打错了。。。)

      1 #include<algorithm>
      2 #include<cstdio>
      3 #include<cstring>
      4 
      5 using namespace std;
      6 
      7 int T,n,no,x,ans=19260817;
      8 int tong[20];
      9 
     10 void dfs(int now)
     11 {
     12     int cnt=0;
     13     for(int i=3;i<=14;i++)
     14     {// shunzi single
     15         if(tong[i]) cnt++;
     16         else cnt=0;
     17         if(cnt>=5)
     18         {
     19             tong[i]--,tong[i-1]--,tong[i-2]--,tong[i-3]--;
     20             int k=i-cnt+1;
     21             for(int j=i-4;j>=k;j--)
     22                 tong[j]--,dfs(now+1);
     23             for(int j=k;j<=i;j++)
     24                 tong[j]++;
     25         } 
     26     }
     27     cnt=0;
     28     for(int i=3;i<=14;i++)
     29     {// shunzi double
     30         if(tong[i]>=2) cnt++;
     31         else cnt=0;
     32         if(cnt>=3)
     33         {
     34             tong[i]-=2,tong[i-1]-=2;
     35             int k=i-cnt+1;
     36             for(int j=i-2;j>=k;j--)
     37                 tong[j]-=2,dfs(now+1);
     38             for(int j=k;j<=i;j++)
     39                 tong[j]+=2;
     40         }
     41     }
     42     cnt=0;
     43     for(int i=3;i<=14;i++)
     44     {// shunzi third
     45         if(tong[i]>=3) cnt++;
     46         else cnt=0;
     47         if(cnt>=2)
     48         {
     49             tong[i]-=3;
     50             int k=i-cnt+1;
     51             for(int j=i-1;j>=k;j--)
     52                 tong[j]-=3,dfs(now+1);
     53             for(int j=k;j<=i;j++)
     54                 tong[j]+=3;
     55         }
     56     }
     57     for(int i=3;i<=15;i++)
     58     {// four with 2
     59         if(tong[i]>=4)
     60         {
     61             tong[i]-=4;
     62             for(int j=3;j<=16;j++)
     63                 if(tong[j])
     64                 {//2 single
     65                     tong[j]--;
     66                     for(int k=3;k<=16;k++)
     67                         if(tong[k])
     68                         {
     69                             tong[k]--;
     70                             dfs(now+1);
     71                             tong[k]++;
     72                         }
     73                     tong[j]++;
     74                 }
     75             for(int j=3;j<=15;j++)
     76                 if(tong[j]>=2)
     77                 {//2 double
     78                     tong[j]-=2;
     79                     for(int k=3;k<=15;k++)
     80                         if(tong[k]>=2)
     81                         {
     82                             tong[k]-=2;
     83                             dfs(now+1);
     84                             tong[k]+=2;
     85                         }
     86                     tong[j]+=2;
     87                 }
     88             dfs(now+1);
     89             //three card
     90             tong[i]+=4;
     91         }
     92     }
     93     for(int i=3;i<=15;i++)
     94     {
     95         if(tong[i]>=3)
     96         {
     97             tong[i]-=3;
     98             for(int j=3;j<=16;j++)
     99                 if(tong[j])
    100                 {//three with 1
    101                     tong[j]--;
    102                     dfs(now+1);
    103                     tong[j]++; 
    104                 }
    105             for(int j=3;j<=15;j++)
    106                 if(tong[j]>=2)
    107                 {//three with 2
    108                     tong[j]-=2;
    109                     dfs(now+1);
    110                     tong[j]+=2;
    111                 }
    112             dfs(now+1);// 3 single
    113             tong[i]+=3;
    114         }
    115     }
    116     if(tong[16]==2) now++;
    117     else if(tong[16]==1) now++;
    118     for(int i=3;i<=15;i++) if(tong[i]) now+=tong[i]>>1;
    119     for(int i=3;i<=15;i++) if(tong[i]) now+=tong[i]&1;
    120     ans=min(ans,now);
    121 }
    122 
    123 int main()
    124 {
    125     scanf("%d%d",&T,&n);
    126     while(T--)
    127     {
    128         for(int i=1;i<=n;i++)
    129         {
    130             scanf("%d%d",&x,&no);
    131             if(x==1) x=14;
    132             else if(x==2) x=15;
    133             else if(x==0) x=16;
    134             tong[x]++;
    135         }
    136         dfs(0);
    137         printf("%d
    ",ans);
    138         ans=19260817;
    139         memset(tong,0,sizeof(tong));
    140     }
    141     return 0;
    142 } 
    AC-landlord

    Day1 预计得分:230

        实际得分:200

    考场上感觉还是有点松懈,第三题没好好想,暴力还写错了,考场上一定要全身心投入啊qwq

    Day2

    T1:跳石头

    思路:裸的二分答案。“最大距离最小”暗示着我们二分的性质,写check函数也很容易,标准的第一题难度。

    预计得分:100分

    实际得分:100分

    (*Add:写的二分的边界细节好像还是不准,还需要多写一些二分题巩固qwq)

     1 #include<cstdio>
     2 #include<algorithm>
     3 
     4 using namespace std;
     5 
     6 int lin,n,m;
     7 int seq[100000];
     8 
     9 bool check(int x)
    10 {
    11     int pre=0,cnt=0;
    12     for(int i=1;i<=n;i++)
    13     {
    14         if(seq[i]-pre<x) cnt++;
    15         else pre=seq[i];
    16         if(cnt>m) return 0; 
    17     }
    18     return 1;
    19 }
    20 
    21 int main()
    22 {
    23 
    24     scanf("%d%d%d",&lin,&n,&m);
    25     for(int i=1;i<=n;i++)
    26         scanf("%d",&seq[i]);
    27     seq[n+1]=lin;
    28     int l=0,r=lin;
    29     while(l<r)
    30     {
    31         int mid=(l+r+1)>>1;
    32         if(check(mid)) l=mid;
    33         else r=mid-1;
    34     }
    35     printf("%d
    ",l);
    36     return 0;
    37 }
    stone

    T2:子串

    思路:本来dp就弱,字符串也没学多少,二者加起来就mengbier了...读题没有多久就弃疗去看第三题了,结果第三题一打就是几乎三个小时...考前10分钟把k=1的暴力打了,本想苟一苟把k=2的也写上,结果时间也不够了。

    预计得分:10分

    实际得分:10分

    正解:方案数,dp。有经验的dalao可我不是可以轻松推出:设f[i][j][k]表示A串匹配到第i位,B串匹配到第j位,用了k个子串 的方案数。并显然有f[i][j][k]=f[i-1][j-1][k-1]+f[i-1][j-1][k],但是我们冷静分析会发现这个转移是不靠谱的。

    首先是它的正确性:上述转移只有在A[i]==B[j]时才成立,于是我们就遗弃掉了没匹配上的情况。

    我们可以再开一个辅助转移数组,s[i][j][k]表示一定用到了当前字符A[i]的方案数,f[i][j][k]表示用或不用当前字符的方案数。

    分析s数组的转移:转移的前提是A[i]==B[j],既然A[i]一定用上了,那么有独自成一串,和与前面组合成一串的两种情况。

    那么有s[i][j][k]=f[i-1][j-1][k-1]+s[i-1][j-1][k]。如果不能转移,则为0.(不能忽视的一步!!后面与滚动数组相关)

    再分析f数组的转移:可由使用当前字符和不使用当前字符转移过来.

    那么有f[i][j][k]=s[i][j][k]+f[i-1][j][k]。

    至此我们成功解决了转移的正确性。

    但是显然它是会超空间的,dp的优化我们考虑状态和转移,状态没有可优化的地方,那么我们考虑转移。观察到转移的第一维只与上一值有关,我们就可以用滚动数组。然后我是第一次写滚动数组==,其实就是把第一维改成两个量pre和now,转移后交换pre和now,达到i-1的效果。

    以及不要忘记赋初值。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int M=1010,mod=1e9+7;
     5 int n,m,k,dp[1002][102][102];
     6 char a[M],b[M];
     7 int main()
     8 {
     9     freopen("substring.in","r",stdin);
    10     freopen("substring.out","w",stdout);
    11     scanf("%d%d%d",&n,&m,&k);
    12     scanf("%s",a+1);
    13     scanf("%s",b+1);
    14     if(k==1)
    15     {
    16         int ans=0;
    17         for(int i=1;i<=n-m+1;i++)
    18         {
    19             bool flag=0;
    20             for(int j=1;j<=m;j++)
    21              if(a[i+j-1]!=b[j]){
    22                  flag=1;break;
    23              }
    24             if(!flag)ans++;
    25         }
    26         ans%=mod;
    27         printf("%d
    ",ans);
    28         return 0;
    29     }
    30     else if(k==2)
    31     {
    32         int ans=0;
    33         for(int i=1;i<=n-m+1;i++)//枚举第一次选的开始位置
    34         {
    35             for(int len=1;len<=m-1;len++)//枚举第一次选了几个 
    36             {
    37                 int len1=m-len;
    38                 for(int j=i+len;j<=n-len1+1;j++)//枚举第二次从哪儿开始选 
    39                 {
    40                     bool flag=0;
    41                     int p=0;
    42                     for(int t=i;t<=i+len-1;t++)
    43                     {
    44                         if(a[t]!=b[++p]){
    45                             flag=1;break;
    46                         }
    47                     }
    48                     if(flag)continue;
    49                     for(int t=j;t<=j+len1-1;t++)
    50                     {
    51                         if(a[t]!=b[++p]){
    52                             flag=1;break;
    53                         }
    54                     }
    55                     if(!flag){
    56                         ans++;
    57                         //printf("%d %d %d %d
    ",i,i+len-1,j,j+len1-1);
    58                     }
    59                 } 
    60             }
    61         }
    62         cout<<ans<<endl; 
    63         return 0;
    64     }
    65     else
    66     {
    67         for(int i=1;i<=k;i++)
    68          dp[0][0][i]=1;
    69         for(int i=1;i<=n;i++)
    70          dp[i][0][0]=1;
    71         for(int i=1;i<=m;i++)
    72          dp[0][i][0]=1;
    73         for(int i=1;i<=n;i++)
    74          for(int j=1;j<=m;j++)
    75           for(int t=1;t<=k;t++)
    76           {
    77               if(a[i]==b[j])
    78                dp[i][j][t]=((ll)dp[i][j][t]+dp[i-1][j-1][t]+dp[i-1][j][t-1])%mod;
    79             else dp[i][j][t]=((ll)dp[i][j][t]+dp[i-1][j][t-1])%mod;
    80             printf("%d %d %d %d
    ",i,j,t,dp[i][j][t]);
    81           }
    82         cout<<dp[n][m][k]<<endl;
    83         return 0;
    84     }
    85     
    86 }
    Chemist的30分做法
     1 #include<cstdio>
     2 #include<algorithm>
     3 
     4 using namespace std;
     5 typedef long long ll;
     6 
     7 int n,m,k;
     8 ll moder=1e9+7,f[2][2000][2000],s[2][2000][2000];
     9 char A[2000],B[2000];
    10 
    11 int main()
    12 {
    13     scanf("%d%d%d",&n,&m,&k);
    14     scanf("%s",A+1);
    15     scanf("%s",B+1);
    16     int now=1,pre=0;f[0][0][0]=1;
    17     for(int i=1;i<=n;i++)
    18     {
    19         f[now][0][0]=1;
    20         for(int j=1;j<=m;j++)
    21             for(int q=1;q<=k;q++)
    22             {
    23                 if(A[i]==B[j]) (s[now][j][q]=s[pre][j-1][q]+f[pre][j-1][q-1])%=moder;
    24                 else s[now][j][q]=0;
    25                 (f[now][j][q]=f[pre][j][q]+s[now][j][q])%=moder;
    26             }
    27         swap(now,pre);
    28     }
    29     printf("%lld",f[pre][m][k]%moder);    
    30     return 0;
    31 }
    substring

    T3:运输计划

    考场思路:这题我跟它耗了将近三小时qwq.

    心路历程如下:(考场上真实记录)

    其实感觉m==1的情况还是比较悬的
    要是最短路有多条 ,然后最后找到的那条上的最大值恰好比较小 那不就凉了么==


    然后到3000的数据,开始想n^2算法应该能想出来,后来感觉好像需要n^2logn
    dij的复杂度是多少来着???nlogn?????

    如果是nlogn海星 不是就凉了==

    dij好像更稳一些,但是我一共也没打过几次dij==

    不对,打完dij感觉好像根本不是那么回事 又给删了==
    dij求的是单源最短路 复杂度岂不会变成n^3logn???
    还不如用floyd呢(滑稽)

    那这样好像要用lca了==
    LCA复杂度多少来着???


    好像想出了正解(??)
    但是感觉悬啊
    60分差不多吧...

    最后当然是非正解了==

    预计得分 :0~100分

    实际得分 :10分

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cmath>
      4 #include<queue>
      5 #include<vector>
      6 
      7 using namespace std;
      8 typedef long long ll;
      9 
     10 int n,m,tot,t,x,y,z,noww,chang;
     11 ll odd,ans;
     12 int head[300090],cnt[600090],d[300090],f[300090][20];
     13 struct node{
     14     int to,val,next;
     15 }edge[600090];
     16 queue<int>q;
     17 vector<int>son[300090];
     18 
     19 void add(int x,int y,int z)
     20 {
     21     edge[++tot].to=y;
     22     edge[tot].next=head[x];
     23     head[x]=tot;
     24     edge[tot].val=z;
     25 }
     26 
     27 void init()
     28 {
     29     q.push(1);d[1]=1;
     30     while(!q.empty())
     31     {
     32         int x=q.front();
     33         q.pop();
     34         for(int i=head[x];i;i=edge[i].next)
     35         {
     36             int y=edge[i].to;
     37             if(d[y]) continue;
     38             d[y]=d[x]+1;
     39             f[y][0]=x;
     40             for(int j=1;j<=t;j++)
     41              f[y][j]=f[f[y][j-1]][j-1];
     42             q.push(y);
     43         }
     44     }
     45 }
     46 
     47 int lca(int x,int y)
     48 {
     49     if(d[x]>d[y]) swap(x,y);
     50     for(int i=t;i>=0;i--)
     51      if(d[f[y][i]]>=d[x]) y=f[y][i];
     52     if(x==y) return x;
     53     for(int i=t;i>=0;i--)
     54      if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
     55     return f[x][0];
     56 }
     57 
     58 bool work(int fa,int x)
     59 {
     60     for(int i=head[fa];i;i=edge[i].next)
     61         if(edge[i].to==x)
     62         {
     63             cnt[i]++;
     64             son[noww].push_back(i);
     65             return 1;
     66         }
     67     for(int i=head[fa];i;i=edge[i].next)
     68     {
     69         int y=edge[i].to;
     70         if(y==x)
     71         {
     72             cnt[i]++;
     73             son[noww].push_back(i);
     74             break;
     75             return 1;
     76         } 
     77         if(work(y,x))
     78         {
     79             cnt[i]++;
     80             son[noww].push_back(i);
     81             return 1;
     82         }
     83     }
     84     return 0;
     85 }
     86 
     87 int main()
     88 {
     89     freopen("transport.in","r",stdin);
     90     freopen("transport.out","w",stdout);
     91     scanf("%d%d",&n,&m);
     92     t=log2(n)+1;
     93     for(int i=1;i<=n-1;i++)
     94     {
     95         scanf("%d%d%d",&x,&y,&z);
     96         add(x,y,z),add(y,x,z);
     97     }
     98     init();
     99     for(int i=1;i<=m;i++) 
    100     {
    101         scanf("%d%d",&x,&y);
    102         noww=i;
    103         int fa=lca(x,y);
    104         if(fa!=x) work(fa,x);
    105         if(fa!=y) work(fa,y);
    106     }
    107     for(int i=1;i<=tot;i+=2)
    108         cnt[i]=cnt[i]+cnt[i+1];
    109     for(int i=1;i<=tot;i+=2)
    110     {
    111         ll tmp=cnt[i];
    112         tmp*=edge[i].val;
    113         if(tmp>odd) chang=i,odd=tmp;
    114     }
    115     for(int i=1;i<=m;i++)
    116     {
    117         ll tmp=0;
    118         for(int j=0;j<son[i].size();j++)
    119             if(son[i][j]==chang||son[i][j]==chang+1) continue;
    120             else tmp+=edge[son[i][j]].val;
    121         ans=max(ans,tmp);
    122     }
    123     printf("%d",ans);
    124     return 0;
    125 }
    考场代码 10pts

     但是后来努力学习了一下60分做法(开始是想搞到 60分的)

    60分做法:50pts n² + 10pts m==1,这些方法就是暴力向上跳。

    然后暴力的核心思想:因为这是在树上,所以每个点可以认为有唯一的入度,所以可以预处理出到每个点的边的权值,以及这个点的父亲。

    瞎搞vis数组开的3000,数组还越界了...导致有两个骗分点一直输出0.

      1 #include<cstdio>
      2 #include<algorithm>
      3 #define maxn 100090
      4 
      5 using namespace std;
      6 typedef long long ll;
      7 
      8 int n,m,tot;
      9 int head[maxn],d[maxn],fa[maxn],pre[maxn],vis[3009][3009],fin[maxn];
     10 struct node{
     11     int to,next,val;
     12 }edge[2*maxn];
     13 struct cellur{
     14     int x,y;
     15 }tas[3000];
     16 
     17 ll lmax(ll a,ll b)
     18 {
     19     if(a>b) return a;
     20     else return b;
     21 }
     22 
     23 void add(int x,int y,int z)
     24 {
     25     edge[++tot].to=y;
     26     edge[tot].next=head[x];
     27     head[x]=tot;
     28     edge[tot].val=z;
     29 }
     30 
     31 void dfs(int x)
     32 {
     33     d[x]=d[fa[x]]+1;
     34     for(int i=head[x];i;i=edge[i].next)
     35     {
     36         int y=edge[i].to;
     37         if(y==fa[x]) continue;
     38         pre[y]=edge[i].val;
     39         fa[y]=x;
     40         dfs(y);
     41     }
     42 }
     43 
     44 int subw(int pos,int x,int y)
     45 {
     46     int ans=0;
     47     if(d[x]<d[y]) swap(x,y);
     48     while(d[x]>d[y])
     49     {
     50         ans+=pre[x];
     51         vis[x][pos]=1;
     52         x=fa[x];
     53     }
     54     while(x!=y)
     55     {
     56         ans+=pre[x]+pre[y];
     57         vis[x][pos]=1,vis[y][pos]=1;
     58         x=fa[x],y=fa[y];
     59     }
     60     return ans;
     61 }
     62 
     63 void work1()
     64 {
     65     int odd=0,ans=0; 
     66     int x=tas[1].x,y=tas[1].y;
     67     if(d[x]<d[y]) swap(x,y);
     68     while(d[x]>d[y])
     69     {
     70         ans+=pre[x];
     71         odd=lmax(odd,pre[x]);
     72         x=fa[x];
     73     }
     74     while(x!=y)
     75     {
     76         ans+=pre[x]+pre[y];
     77         odd=lmax(odd,pre[x]);
     78         odd=lmax(odd,pre[y]);
     79         x=fa[x],y=fa[y];
     80     } 
     81     printf("%lld",ans-odd);
     82 }
     83 
     84 void work2()
     85 {
     86     int odd=0;
     87     int minn=-1;
     88     for(int i=1;i<=m;i++) fin[i]=subw(i,tas[i].x,tas[i].y),minn=max(minn,fin[i]);    
     89     for(int i=1;i<=n;i++)
     90     {
     91         //所有路径只能是pre[i]上的  所以枚举这些边就行 
     92         int tmp=0;
     93         for(int j=1;j<=m;j++)
     94             tmp=max(tmp,fin[j]-pre[i]*vis[i][j]);
     95             //vis数组就是这条边有没有在j这个计划中出现过 只能为0或1 
     96         if (minn==-1||minn>tmp)minn=tmp;
     97     }
     98     printf("%d",minn);
     99 }
    100 
    101 int main()
    102 {
    103     scanf("%d%d",&n,&m);
    104     for(int i=1;i<=n-1;i++)
    105     {
    106         int x=0,y=0,z=0;
    107         scanf("%d%d%d",&x,&y,&z);
    108         add(x,y,z),add(y,x,z);
    109     }
    110     for(int i=1;i<=m;i++)
    111         scanf("%d%d",&tas[i].x,&tas[i].y);
    112     fa[1]=1;
    113     dfs(1);
    114     if(m==1)
    115         work1();
    116     else work2();
    117     return 0;
    118 }
    努力骗到的60pts
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cmath>
      4 #include<algorithm>
      5 using namespace std;
      6 const int M=3e5+10,MAX=1e7;
      7 typedef long long ll;
      8 int read()
      9 {
     10     int ans=0;
     11     char ch=getchar(),last=' ';
     12     while(ch<'0'||ch>'9')
     13     {last=ch;ch=getchar();}
     14     while(ch>='0'&&ch<='9')
     15     {ans=ans*10+ch-'0';ch=getchar();}
     16     if(last=='-')ans=-ans;
     17     return ans;
     18 }
     19 int n,m;
     20 ll d[M],ans=1e12;
     21 int num=0,head[M];
     22 struct node{
     23     int beg,end,next,w;
     24 }e[M*2];
     25 struct Node{
     26     int str,fin,cost;
     27 }P[M];
     28 struct PLAN{
     29     int from,to;
     30 }p[M];
     31 void add(int x,int y,int z)
     32 {
     33     num++;
     34     e[num].beg=x;
     35     e[num].end=y;
     36     e[num].w=z;
     37     e[num].next=head[x];
     38     head[x]=num;
     39 }
     40 
     41 int size[M],fa[M],son[M],dep[M];
     42 void dfs1(int x)
     43 {
     44     dep[x]=dep[fa[x]]+1;
     45     size[x]=1;
     46     for(int i=head[x];i;i=e[i].next)
     47     {
     48         int y=e[i].end;
     49         if(y==fa[x])continue;
     50         fa[y]=x;
     51         d[y]=(ll)d[x]+e[i].w;
     52         dfs1(y);
     53         size[x]+=size[y];
     54         if(!son[x]||size[son[x]]<size[y])
     55          son[x]=y;
     56     }
     57 }
     58 
     59 int top[M];
     60 void dfs2(int x,int topfa)
     61 {
     62     top[x]=topfa;
     63     if(!son[x])return;
     64     dfs2(son[x],topfa);
     65     for(int i=head[x];i;i=e[i].next)
     66     {
     67         int y=e[i].end;
     68         if(y==son[x]||y==fa[x])continue;
     69         dfs2(y,y);
     70     }
     71 }
     72 
     73 int LCA(int x,int y)
     74 {
     75     while(top[x]!=top[y])
     76     {
     77         if(dep[top[x]]<dep[top[y]])swap(x,y);
     78         x=fa[top[x]];
     79     }
     80     if(dep[x]>dep[y])swap(x,y);
     81     return x;
     82 }
     83 bool cmp(Node X,Node Y)
     84 {
     85     return X.cost>Y.cost;
     86 }
     87 int main()
     88 {
     89     freopen("transport.in","r",stdin);
     90     freopen("transport.out","w",stdout);
     91     n=read();m=read();
     92     for(int i=1;i<=n-1;i++)
     93     {
     94         int x=read(),y=read(),z=read();
     95         add(x,y,z);add(y,x,z);
     96         P[i].str=x;P[i].fin=y;P[i].cost=z;
     97     }
     98     for(int i=1;i<=m;i++)
     99      p[i].from=read(),p[i].to=read();
    100     dfs1(1);
    101     dfs2(1,1);
    102     if((n<=3000&&m<=3000)||m==1){
    103     //O(nmlogn)枚举暴力,期望得分:55 
    104     for(int i=1;i<=num;i+=2)//枚举改造了哪个航道 
    105     {
    106         int x=e[i].beg,y=e[i].end,A;
    107         if(dep[x]<dep[y])A=x;
    108         else A=y;
    109         ll mx=0;
    110         for(int j=1;j<=m;j++)
    111         {
    112             int B=p[j].from,E=p[j].to;
    113             int lca=LCA(B,E);
    114             ll tim=d[B]+d[E]-2*d[lca];
    115             if((LCA(A,B)==A||LCA(A,E)==E)&&dep[lca]<=dep[A])
    116              tim-=e[i].w;
    117             mx=max(mx,tim);
    118         }
    119         ans=min(ans,mx);
    120     }
    121     cout<<ans<<endl;
    122     }
    123     else{
    124         sort(P+1,P+n,cmp);
    125         for(int i=1;i<=min(n-1,(MAX/m));i++)//枚举改造了哪个航道 
    126         {
    127             int x=P[i].str,y=P[i].fin,A;
    128             if(dep[x]<dep[y])A=x;
    129             else A=y;
    130             ll mx=0;
    131             for(int j=1;j<=m;j++)
    132             {
    133                 int B=p[j].from,E=p[j].to;
    134                 int lca=LCA(B,E);
    135                 ll tim=d[B]+d[E]-2*d[lca];
    136                 if((LCA(A,B)==A||LCA(A,E)==E)&&dep[lca]<=dep[A])
    137                  tim-=P[i].cost;
    138                 mx=max(mx,tim);
    139             }
    140             ans=min(ans,mx);
    141         }
    142         cout<<ans<<endl;
    143     }
    144     fclose(stdin);fclose(stdout);
    145     return 0;
    146 }
    Chemist的70pts骗分法

    正解:二分答案+lca+树上差分

    一句话题意:给你许多树上的链,要求把树上其中一条边变为0后链权值的最大值最小。

     这个题出的二分还是十分隐秘的qwq,(比如zhanggenchen篱落疏疏一径深那题)因为题目要我们求计划最大值最小,所以满足二分单调性。

    我们就可以二分这个最终的答案。设这个答案为mid,则所有长度>mid的路径上都至少需要删除一条边,对这些路径求交,最优方案是删去路径中长度最大的边。如果删去这条最长边后最长路径还有大于mid的,那么这个答案不合法。

    问题的关键转化为求树上路径交,我们可以使用树上差分(现学的)。

    另外这里预处理各个计划的链长我用到的是树上倍增求LCA+dfs。

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 #include<queue>
      5 #include<cmath>
      6 #define maxn 300090
      7 // long long?
      8 using namespace std;
      9 
     10 int n,m,t,tot,maxlen,l,r,num,ret;
     11 int pre[maxn],head[maxn],d[maxn],f[maxn][20],val[maxn],vis[maxn],dis[maxn];
     12 struct node{
     13     int to,next,val;
     14 }edge[maxn*2];
     15 struct cellur{
     16     int x,y,len,lca;
     17 }tas[maxn];
     18 
     19 void add(int x,int y,int z)
     20 {
     21     edge[++tot].to=y;
     22     edge[tot].next=head[x];
     23     head[x]=tot;
     24     edge[tot].val=z;
     25 }
     26 
     27 void LCA_prework()
     28 {
     29     queue<int>q;
     30     q.push(1);d[1]=1;
     31     while(!q.empty())
     32     {
     33         int u=q.front();q.pop();
     34         for(int i=head[u];i;i=edge[i].next)
     35         {
     36             int v=edge[i].to;
     37             if(d[v]) continue;
     38             d[v]=d[u]+1;
     39             f[v][0]=u;
     40             pre[v]=edge[i].val;
     41             for(int j=1;j<=t;j++)
     42                 f[v][j]=f[f[v][j-1]][j-1];
     43             q.push(v);
     44         }
     45     }
     46 }
     47 
     48 int LCA(int x,int y)
     49 {
     50     if(d[x]>d[y]) swap(x,y);
     51     for(int i=t;i>=0;i--)
     52         if(d[f[y][i]]>=d[x]) y=f[y][i];
     53     if(x==y) return x;
     54     for(int i=t;i>=0;i--)
     55         if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
     56     return f[x][0];
     57 }
     58 
     59 void dfs(int x)
     60 {
     61     vis[x]=1;
     62     for(int i=head[x];i;i=edge[i].next)
     63     {
     64         int y=edge[i].to;
     65         if(vis[y]) continue;
     66         dis[y]=dis[x]+edge[i].val;
     67         dfs(y);
     68     }
     69 }
     70 
     71 void review(int u,int fa)
     72 {
     73     for(int i=head[u];i;i=edge[i].next)
     74     {
     75         int v=edge[i].to;
     76         if(v==fa) continue;
     77         review(v,u);
     78         val[u]+=val[v];
     79     }
     80     if(val[u]==num&&pre[u]>ret)
     81         ret=pre[u];//记录路径中最长的边 
     82 }
     83 
     84 bool check(int x)
     85 {
     86     memset(val,0,sizeof(val));
     87     num=ret=0;
     88     for(int i=1;i<=m;i++)
     89     {
     90         if(tas[i].len>x)
     91         {
     92             val[tas[i].x]++;
     93             val[tas[i].y]++;
     94             val[tas[i].lca]-=2;
     95             num++;
     96         }
     97     }
     98     review(1,0);
     99     if(maxlen-ret>x) return 0;
    100     return 1;
    101 }
    102 
    103 int main()
    104 {
    105     scanf("%d%d",&n,&m);
    106     t=log2(n)+1;
    107     for(int i=1;i<=n-1;i++)
    108     {
    109         int x=0,y=0,z=0;
    110         scanf("%d%d%d",&x,&y,&z);
    111         add(x,y,z);add(y,x,z);
    112     }
    113     for(int i=1;i<=m;i++)
    114         scanf("%d%d",&tas[i].x,&tas[i].y);
    115     LCA_prework();
    116     for(int i=1;i<=n;i++)
    117         if(!vis[i]) dfs(i);
    118     for(int i=1;i<=m;i++)
    119     {
    120         int fa=LCA(tas[i].x,tas[i].y);
    121         tas[i].lca=fa;
    122         tas[i].len=dis[tas[i].x]+dis[tas[i].y]-2*dis[fa];
    123         maxlen=max(maxlen,tas[i].len);
    124     }  
    125     r=maxlen;
    126     while(l<r)
    127     {
    128         int mid=(l+r)>>1;
    129         if(check(mid)) r=mid;
    130         else l=mid+1;
    131         //printf("%d %d
    ",ret,num);
    132     }
    133     printf("%d",l);
    134     return 0;
    135 }
    View Code

    (但是不开longlong好像也没关系的样子...)

    Day2 预计得分:100+10+0~100=110~210

        实际得分:100+10+10=120

    感觉考场上不能像我今天一样再孤注一掷写T3正解了吧...,拿个稳妥的暴力分也是好的呀...。所以今天的时间分配出现了很大问题,第二题虽然我dp很不好应该至少还能加上20分k=2的情况吧,实在布星T3把所以m==1的情况都打出来,顺便再打个n==100的情况也比我现在的结果强啊qwq。考试策略和技巧还有待改善。

    扯些别的:

    现在感觉学习了那么多算法,目的当然是尽量在考场上打出正解。但是事实是,T2正解都很难想全,T3正解就更难了。OI赛制中暴力分,部分分还是王道。学习那么多算法,也是让我们在考场上掌握更多优雅的暴力方法,得到更多的分啊。比如ChemistDay2T3打了一个大概是树剖的东西吧,搞到了60pts(?,而我不会树剖,更不会从树的链角度出发分析==

    然后感觉现在基础并不牢固==一些算法掌握的也不牢==比如滚动数组优化当初学长讲了但没写,搜索缺少方法,动规更是一塌糊涂==。对照学长给的noip知识表好像没有多少算法敢说自己掌握的特别牢==,比如树上倍增/差分。

    还有一些实用技巧也并不会==,比如生成数据手打就有时候不会,(已加入todolist),有的算法复杂度不确定等等==

  • 相关阅读:
    分页,上传,下载
    java web 开发模式
    EL/JSTL-jsp页面更简单的输出方式
    过滤器和监听器
    Servlet
    jsp标准动作
    java Bean
    寻找你的热情(1)——检查自己所处的位置
    fedora25 安装sublime text3
    python实例3-天气小模块
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9648542.html
Copyright © 2020-2023  润新知