2015-2016 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2015)

题目链接 :  http://codeforces.com/gym/100781/attachments

 

A-Adjoin the Network

题意大概是有多棵树(也可能是一颗),现在要把它们合并成一颗树,且保证最终这棵树的最远点对最小,求最后的这棵树的最长链(最远点对)的长度。

练习赛时想的是我们在每棵树上跑出最长链,并找到中间点,然后以所有"最长链"中的最长链的中间点为根节点,其他"最长链"的中间点直接与这个根节点相连即可构造出最远点对最小的大树。然而并不知道怎么找到最长链的中间点,跑最长链也有点不熟。

其实这题只要有上面构造的思路,然后求出所有最长链的长度即可。因为最后这棵最远点对最小的大树其最长链的只可能有三种情况(对应三种推出方式):

1. 最长链的长度

2. 最长链的"一半"长度 + 次长链的"一半"长度 + 1

3. 次长链的"一半"长度 + 季长链的"一半"长度 + 1

ps : 以上的"一半"是偶取半,奇取半+1 这个画下图就能明白

然后我们只需要跑出每棵小树的"最长链"长度排序计算即可。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define pb(x) push_back((x));
 4 
 5 const int INF=0x3f3f3f3f;
 6 vector< int > G[100000+5];
 7 bool vis[100000+5];
 8 int d[100000+5][3];
 9 vector< int > lian;
10 int longest;
11 
12 void dfs1(int me, int fa){
13     vis[me]=true;
14     for(auto son:G[me]){
15         if(son==fa)    continue;
16         dfs1(son,me);
17         if(d[son][0]+1>=d[me][0]){
18             d[me][1]=d[me][0];
19             d[me][0]=d[son][0]+1;
20         }
21     }
22 }
23 
24 void dfs2(int me ,int fa){
25     if(fa!=-1){
26         if(d[me][0]+1==d[fa][0]) d[me][2]=max(d[me][0]+d[me][1],d[me][0]+1+d[fa][1]);
27         else d[me][2]=max(d[me][0]+d[me][1],d[me][0]+1+d[fa][0]);
28     }
29     else{
30         d[me][2]=d[me][0]+d[me][1];
31     }
32     for(auto son: G[me]){
33         if(son==fa)    continue;
34         dfs2(son,me);
35     }
36     longest=max(longest,d[me][2]);
37 }
38 
39 bool cmp(int a,int b){
40     return a>b;
41 }
42 
43 int getR(int L){
44     if(L%2) return L/2+1;
45     return L/2;
46 }
47 
48 int main(){
49     int N,M;
50     scanf("%d%d",&N,&M);
51     for(int i=0;i<N;++i) {
52         G[i].clear();
53         vis[i]=false;
54     }
55     lian.clear();
56     for(int i=0;i<M;++i){
57         int u,v;
58         scanf("%d%d",&u,&v);
59         G[u].pb(v);
60         G[v].pb(u);
61     }
62     for(int i=0;i<N;++i){
63         if(vis[i]==false){
64             longest=-1;
65             dfs1(i,-1);
66             //printf("%d : %d\n",i,d[i][0]+d[i][1]);
67             dfs2(i,-1);
68             lian.pb(longest);
69         }
70     }
71     sort(lian.begin(),lian.end(),cmp);
72     int ans=-1;
73     if(lian.size()>0) ans=max(ans,lian[0]);
74     if(lian.size()>1) ans=max(ans,getR(lian[0])+getR(lian[1])+1);
75     if(lian.size()>2) ans=max(ans,getR(lian[1])+getR(lian[2])+2);
76     printf("%d\n",ans);
77     return 0;
78 }
View Code

开始的时候只用一次dfs跑,这只能跑出根节点的最长链,还要第二次dfs。 简单说明: d[i][0]为i点固定方法最深度 ,d[i][1]为次深度,d[i][2]为经过i点的最长链长度。

(求最远点对的方法以前学过,这题应该很快写出来才是,惭愧惭愧。

 

 

 E-Entertainment Box

最开始学贪心的例子是给出一系列课程(知道开始与结束时间),需要尽可能安排上更多的课程,解决方案是贪心结束时间早的。

而这道题相当于给了N个时间段,K个轨道可以跑时间段,要求能上最多的课程数。(题目是录节目,我说成上课,其实一个意思)

想法还是贪心结束时间靠前的,用一个数据结构模拟K个轨道跑课程。

错误代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define fst first
 4 #define scd second
 5 
 6 typedef long long ll;
 7 typedef pair<ll , ll > pii;
 8 queue< pii > Q;
 9 pii pro[100000+5];
10 int _id[100000+5];
11 
12 bool cmp(int i,int j){
13     if(pro[i].scd!=pro[j].scd)return pro[i].scd<pro[j].scd;
14     return pro[i].fst<pro[i].fst;
15 }
16 
17 int main(){
18     int N,K;
19     while(~scanf("%d%d",&N,&K)){
20     //printf("N K :%d %d\n",N,K);
21     for(int i=1;i<=N;++i){
22         ll xx,yy;
23         scanf("%lld%lld",&xx,&yy);
24         _id[i]=i;
25         pro[i]=make_pair(xx,yy);
26     }
27     sort(_id+1,_id+1+N,cmp);
28     while(!Q.empty()) Q.pop();
29     //for(int i=1;i<=N;++i) printf("record %d : %lld %lld\n",i,pro[_id[i]].fst,pro[_id[i]].scd);
30     int cnt=0;
31     for(int i=1;i<=K;++i) Q.push(make_pair(-1ll,0ll));
32     //printf("N K :%d %d\n",N,K);
33     for(int i=1;i<=N;++i){
34         pii can=pro[_id[i]];
35         while((!Q.empty())&&can.fst>=Q.front().scd) Q.pop();
36         if(Q.size()<K){
37             Q.push(can);
38             //printf("record %d : %lld %lld\n",i,pro[_id[i]].fst,pro[_id[i]].scd);
39             cnt++;
40         }
41     }    
42     printf("%d\n",cnt);
43 }
44     return 0;
45 }
View Code

插入新课程时队列会弹出所有已经早于新课程开始时间就结束的课程,然而这是不行的。

5 3

1 2

1 3

1 5

4 6

1 7

这是队友出的Hack数据,如果(4  6) 入队 ,(1  2) 和(1  3)都会被弹出,然后 (1, 7)  反而可以被多计一次了。

 

正确更换课程应该最多更换一个早于新课程开始时间就结束的且最近的课程。这时候我们需要用 multiset 。(之前队列不行也可以理解为我们已经贪结束时间了,不能随便按自然时间流逝弹出所有早于新课程开始时间就结束的课程)

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define fst first
 4 #define scd second
 5 typedef pair<int ,int > pii;
 6 multiset <int > mtst;
 7 
 8 bool cmp(pii a, pii b){
 9     if(a.scd!=b.scd) return a.scd<b.scd;
10     return a.fst<b.fst;
11 }
12 
13 pii P[100000+5];
14 
15 int main(){
16     int N,K;
17     scanf("%d%d",&N,&K);
18     for(int i=0;i<N;++i){
19         scanf("%d%d",&P[i].fst,&P[i].scd);
20         //printf("%d : %d %d\n",i,P[i].fst,P[i].scd);
21     }
22     sort(P,P+N,cmp);
23     //for(int i=0;i<N;++i) printf("%d : %d %d\n",i,P[i].fst,P[i].scd);
24     for(int i=0;i<K;++i){
25         mtst.insert(0);
26     }
27     multiset< int > :: iterator p;
28     int cnt=0;
29     for(int i=0;i<N;++i){
30         int l=P[i].fst,r=P[i].scd;
31         p=mtst.upper_bound(l);
32         if(p==mtst.begin()) continue;
33         p--;
34         mtst.erase(p);
35         mtst.insert(r);
36         //printf("r : %d\n",r);
37         cnt++;
38     }
39     printf("%d\n",cnt);
40     return 0;
41 }
View Code

 

posted on 2018-07-27 13:59  Emiya_Kiritsugu  阅读(455)  评论(0编辑  收藏  举报

导航