解題報告: Codeforces Round #527 (Div. 3)

好久沒打CF了,低迷了一段時間後又忙於搬磚和摸魚等等0_0

工作後發現有時間寫寫題和題解也是一種享受的,當然水平還是一如既往的菜的...


C、Prefixes and Suffixes

題目大意:有一個長度爲n的字符串,給出分別長度爲1~n-1的前綴和後綴的亂序排列,總共有(2n-2)個排列,問每個給出的排列是前綴還是後綴。

思路:由最長的兩個n-1的排列可以確定4種字符串,因爲數據也不大,生成後依次進行檢測是否滿足要求即可。

代碼:

#include<bits/stdc++.h>

#define _Out for(int i=0;i<sum;++i)printf("%c",ans[i]?'P':'S')
#define fi first
#define se second

using namespace std;

int n,sum;
vector< pair<string,int> >S[100];
bool ans[200];



bool check(string str){
   for(int i=1;i<n;++i){
      if(str.find(S[i][0].fi)==0 && str.find(S[i][1].fi,n-i)==n-i){
         ans[S[i][0].se]=1;
         ans[S[i][1].se]=0;
      }else if(str.find(S[i][1].fi)==0 && str.find(S[i][0].fi,n-i)==n-i){
         ans[S[i][1].se]=1;
         ans[S[i][0].se]=0;
      }else return false;
   }return true;
}

int main()
{
   scanf("%d",&n);
   sum = (n<<1)-2;
   for(int i=0;i<sum;++i){
      string tmp;
      cin>>tmp;
      S[tmp.length()].push_back(make_pair(tmp,i));
   }

   if(check(S[n-1][0].fi+S[n-1][1].fi[n-2]))_Out;
   else if(check(S[n-1][0].fi[0] + S[n-1][1].fi))_Out;
   else if(check(S[n-1][1].fi+S[n-1][0].fi[n-2]))_Out;
   else if(check(S[n-1][1].fi[0] + S[n-1][0].fi))_Out;
}

D1. Great Vova Wall (Version 1)

題目大意:類似於俄羅斯方塊,有任意數量個2*1的方塊,可以選擇橫放或者豎放,現在有一個寬度爲n,高度不限的平面。給出每個位置的方塊高度,問有沒有一種放置方塊的方式可以使得放滿某一高度下的所有位置。

思路:

因爲可以豎放,所以整個空間的高度可以視作模2.

從左往右依次進行維護,儘量使得左邊的空間都放滿,可以發現最後的情況一定是兩種:

  1. 可以在某一高度放滿
  2. 右上的部分呈鋸齒狀

根據當前的高度和左邊的高度的奇偶關係,結合左邊空間的放置情況,可以做出以下操作:

  1. 奇偶相同,左邊放滿:當前放滿,奇偶不變
  2. 奇偶相同,左邊不滿:鋸齒數量減一,奇偶變化(這裏不明白的話,畫幾個例子)
  3. 奇偶不同,左邊放滿:

代碼:

#include<bits/stdc++.h>

using namespace std;

int main()
{
   int type = 0;
   int n,a,b,row=0;
   scanf("%d%d",&n,&a);a&=1;
   for(int i=1;i<n;++i,row^=1){
      scanf("%d",&b);b&=1;
      if(a==b){
         if(type>=1){
            type-=1;
            a^=1;
         }
      }
      else{
         if(type||!row)++type;
         a ^= 1;
      }
   }printf("%s\n",type?"NO":"YES");
   return 0;
}


D2. Great Vova Wall (Version 2)

題目大意:規則同上,只是不能豎放,最後詢問能否放滿

思路:

若當前方格高度大於左邊的方格高度,必定無法放滿。

若當前方格高度等於左邊的方格高度,那麼可以變爲任意大於當前的高度。

那麼維護一個遞減棧,當出現等於的情況,就向棧底進行合併即可。

代碼:

#include<bits/stdc++.h>

#define fi first
#define se second

using namespace std;

stack<pair<int,int> > S;

void oper(){
   auto t = S.top();S.pop();
   while(!S.empty()){
      S.top().se+=t.se;
      t = S.top();S.pop();
      if(t.se&1)break;
   }S.push(t);
}

int main()
{
   int n,x;
   scanf("%d%d",&n,&x);
   S.push(make_pair(x,1));
   for(int i=1;i<n;++i){
      scanf("%d",&x);
      if(S.top().fi>x){
         S.push(make_pair(x,1));
      }else if(S.top().fi==x){
         S.top().se+=1;
         oper();
      }else {
         if((S.top().se%2)==0){
            S.top()=make_pair(x,S.top().se+1);
            oper();
         }else {
            S.push(make_pair(x,1));
            break;
         }
      }
   }printf("%s\n",S.size()>1?"NO":"YES");
}

E. Minimal Diameter Forest

題目大意:給出一片森林,自由的進行連點使其變成一棵樹,詢問能得到的最短的樹上最長距離。

思路:先找森林裏每棵樹上到最遠節點距離最短的點(忘記怎麼叫了),然後按最遠距離進行排序,這時一個點就代表了一棵樹,把最大的樹和其餘的相連,處理好連接到最大的樹的點的最長距離以及次長距離就可以了

代碼:

#include<cstdio>
#include<vector>
#include<queue>
#include"bits/stdc++.h"


#define pii pair<int,int>
#define fi first
#define se second
using namespace std;

int n,m,ans;

int root[1005],dis[1005];
vector<int>E[1005];
map< int,pii >mi;
inline int _find(int x){return x!=root[x]?root[x]=_find(root[x]):x;}
priority_queue< pii > Q;
bool vis[1005];
vector< pii > V;

void GetLongDis(int x){
   int len = -1 , y = x , fy = _find(x);
   queue<int>S;
   S.push(x);
   memset(vis,0,sizeof(vis));
   vis[x] = true;
   while(!S.empty()){
      int sz = S.size();
      ++len;
      while(sz--){
         x = S.front();S.pop();
         for(int i=0;i<E[x].size();i++){
            int& t = E[x][i];
            if(vis[t])continue;
            vis[t] = true;
            S.push(t);
         }
      }
   }
   auto iter = mi.find(fy);
   ans = max(ans , len);
   if(iter==mi.end())mi[fy] = make_pair(len,y);
   else iter->se = min(iter->se,make_pair(len,y));

}

int main()
{
   scanf("%d%d",&n,&m);
   for(int i=1;i<=n;++i)root[i]=i;
   for(int a,b,i=0;i<m;++i){
      scanf("%d%d",&a,&b);
      E[a].push_back(b);
      E[b].push_back(a);
      int fa = _find(a) , fb = _find(b);
      if(fa!=fb)root[fa]=fb;
   }

   for(int i=1;i<=n;++i)
      GetLongDis(i);

   for(auto iter : mi)
      Q.push( make_pair( iter.se.fi , iter.se.se ) );
   pii tmp = Q.top();Q.pop();
   int max1 = tmp.fi , max2 = 0 ;
   while(!Q.empty()){
      V.push_back(make_pair(tmp.se,Q.top().se));
      max2 = max(max2,Q.top().fi+1);
      if(max2>max1)swap(max1,max2);
      Q.pop();
   }ans = max(ans,max1+max2);
   printf("%d\n",ans);
   for(auto iter: V)
      printf("%d %d\n",iter.fi,iter.se);
   return 0;
}


F. Tree with Maximum Cost

題目大意:給定一棵樹,以及樹上每個節點的值。求一個最大的樹的代價:選定一個點,對應的代價爲到其餘點的邊數量乘以那個點的值 之和。

思路:

比較基礎的樹形DP,先得到根的代價後,轉移的時候考慮一下對應的邊的帶來的貢獻。

代碼:

#include<bits/stdc++.h>

#define N 200005
#define LL long long
using namespace std;


int n;
vector<int>E[N];
LL sum,son[N],ans[N],val[N],ANS;


void dfs_1(int v=1,int fa=0,int dis=0){
   ans[1] += val[v] * dis ;
   for(int i=0;i<E[v].size();++i){
      int &t = E[v][i];
      if(t==fa)continue;
      dfs_1(t,v,dis+1);
      son[v]+=son[t]+val[t];
   }
}

void dfs_2(int v=1,int fa=0){
   for(int i=0;i<E[v].size();++i){
      int &t = E[v][i];
      if(t==fa)continue;
      ans[t] = ans[v] - (val[t] + son[t])*2 + sum ;
      dfs_2(t,v);
   }ANS = max(ANS,ans[v]);

}

int main(){
   scanf("%d",&n);
   for(int i=1;i<=n;++i){
      scanf("%lld",&val[i]);
      sum+=val[i];
   }
   for(int i=1,a,b;i<n;++i){
      scanf("%d%d",&a,&b);
      E[a].push_back(b);
      E[b].push_back(a);
   }dfs_1();dfs_2();
   printf("%lld\n",ANS);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章