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("\n");
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\n",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\n",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\n",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\n",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\n",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\n",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),有的算法复杂度不确定等等==

猜你喜欢

转载自www.cnblogs.com/nopartyfoucaodong/p/9648542.html