2019-2020 ICPC Southwestern European Regional Programming Contest (SWERC 2019-20)部分題解

比賽鏈接
躺狗實錘,只會兩題
G.Swapping Places
大意:給你一個長度爲nn的字符串序列,字符串種類爲ss,給你ll組關係。每組關係由兩個字符串a,ba,b構成,若aba,b相鄰,則可以交換這兩個字符串。問你這個字符串序列的最終能 變成的字典序最小的序列是什麼。
思路:字符串不好看,所以先離散化成數組再搞。
一個比較明顯的思路是 由於種類數最多200,所以可以考慮枚舉每一位最小能放的字符串是什麼就行。
然後考慮具體實現。
我們考慮在枚舉到第i位的時候,放第j小的字符串是否合法?[1,i1][1, i-1]位已經放完了。那麼肯定是要把下標最小的且沒用過的jj移到第ii位,那麼顯然,1i1-i中所有沒用過的字符串都要能與j互相交換纔行。暴力來checkcheck顯然是不可取的。一種可以實現的優化方式是,當用過一個第xx位的字符串的時候,xx位的字符串會對x+1nx+1-n的字符串產生影響,因爲,後面的字符串可能因爲這個字符串而不能移動到xx位之前。那麼用一個bitbit維護第ii種字符串在區間[1,r]1rn[1,r],1\leq r\leq n內已經用掉的且不能交換的字符串數量。
之前預處理一下每一位上的字符串,在他前面不能交換的字符串的數量 就行。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
#define fi first
#define se second
#define pb push_back
string a[N],c[N];
int n,b[N],s,l,ed[222][222],ans[N];
vector<int>v[202];
int len[202],vis[N],cnt[N],dis[N];
int t[202][N];
void add(int x,int y,int z){
  for(;y<N;y+=y&-y)t[x][y]+=z;
}
int get(int x,int y){
  int z=0;
  for(;y;y-=y&-y)z+=t[x][y];
  return z;
}
int main() {
  ios::sync_with_stdio(false);
  cin>>s>>l>>n;
  for(int i=1;i<=s;i++){
    cin>>a[i];
    c[i]=a[i];
    ed[i][i]=1;
  }
  sort(c+1,c+1+s);
  for(int i=1;i<=l;i++){
    string x,y;
    cin>>x>>y;
    int dx=lower_bound(c+1,c+1+s,x)-c;
    int dy=lower_bound(c+1,c+1+s,y)-c;
    ed[dx][dy]=1;
    ed[dy][dx]=1;
  }
  for(int i=1;i<=n;i++){
    string x;
    cin>>x;
    b[i]=lower_bound(c+1,c+1+s,x)-c;
    v[b[i]].pb(i);
  }
  for(int i=1;i<=n;i++){
    for(int j=1;j<=s;j++){
      if(!ed[b[i]][j])dis[i]+=cnt[j];
    }
    cnt[b[i]]++;
  }
  for(int i=1;i<=n;i++){
    for(int j=1;j<=s;j++){
      if(len[j]>=v[j].size())
        continue;;
      int x=v[j][len[j]];
      int st=0;
      if(dis[x]!=get(j,x)){
        st=1;
      }
      if(!st){
        ans[i]=j;
        vis[x]=1;
        len[j]++;
        for(int k=1;k<=s;k++){
          if(!ed[j][k]){
            add(k,x,1);
          }
        }
        break;
      }
    }
  }
  for(int i=1;i<=n;i++){
    cout<<c[ans[i]]<<' ';
  }
  return 0;
}

K.Birdwatching
題意:自己讀
思路:
暴力的做法:枚舉每一條跟t的邊,然後隨便dfs一下。
顯然會T。
那麼仔細考慮一下。如果一條邊(s,t)(s,t)不合法的話,那麼ss必然可以通過經過其他邊到達tt,就會存在類似這樣的路徑s>....>q>ts->....->q->t
那我們建反邊,從與t相鄰的點出發dfs,設起始點爲aa,dfs的過程中不能第二次經過aa,剩下能到達的點顯然都可以通過x>....>a>tx->....->a->t的路徑到達t,所以都是不合法的點。另外每個點在所有dfs中最多隻需要訪問兩次。因爲,在從某個起始點出發之後,可以訪問到其他點,但是,這個起始點需要其他起始點來check自己是不是合法點。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
vector<int> e[N],ans,V;
int vis[N],n,m,t,is_link[N],al[N];
void dfs(int x,int _y){
  for (auto y : e[x]){
    if (vis[y] || y==_y ||al[y]>1)
      continue;
    V.pb(y);
    al[y]++;
    vis[y] = 1;
    is_link[y]=1;
    dfs(y,_y);
  }
}
int main(){
  ios::sync_with_stdio(false);
  cin>>n>>m>>t;
  for(int i=1;i<=m;i++){
    int x,y;
    cin>>x>>y;
    e[y].pb(x);
  }
  vis[t] = 1;
  for (auto x : e[t]){
    V.clear();
    dfs(x,x);
    for(auto k:V)vis[k]=0;
  }
  for (auto x : e[t]){
    if (!is_link[x]){
      ans.pb(x);
    }
  }
  cout<<(int)ans.size()<<'\n';
  sort(ans.begin(),ans.end());
  for (auto x : ans){
    cout<<x<<'\n';
  }
  return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章