ABC401

AtCoder Beginner Contest 401

D - Logical Filling

不妨先考虑全是?的情况 . 若长度为偶数, 显然所有位置都应该是? .

奇数时 : 以5为例 , 若要有3个o , 则只能为o.o.o .

具体地 : 我们将原来的串转化一下 , 若与之相邻存在o,其一定是.,然后分割出一段一段的?,偶数长度不用管 , 记录一下最多能放多少个o , 以及需要放多少个o, 若恰好相等则每一个位置都固定了. 否则不用管. 特别地 ,若不用放o,需要将所有位置换成.

void solve() {
  int n,k;cin>>n>>k;
  string s;cin>>s;
  s="&"+s;
  vector<pair<int,int>> p;
  for (int i=1;i<=n;++i) {
    if (s[i]=='o') k--;
    if (s[i]=='?') {
      if (s[i-1]=='o') s[i]='.';
      if (i+1<=n and s[i+1]=='o') s[i]='.';
    }
  }
  int cnt=0;
  for (int i=1;i<=n;++i) {
    if (s[i]=='?') {
      int r=i;
      while (r<=n and s[r]=='?') r++;
      r--;
      cnt+=(r-i+2)/2;
      if ((r-i+1)&1) {
        p.push_back({i,r});
      }
      i=r;
    }
  }
  if (cnt==k) {
    for (auto [l,r]:p) {
      for (int i=1;i<=r-l+1;++i) {
        if (i&1) s[l-1+i]='o';
        else s[l-1+i]='.';
      }
    }
  }
  if (k==0) {
    for (int i=1;i<=n;++i) {
      if (s[i]=='?') s[i]='.';
    }
  }
  cout<<s.substr(1);
}

E - Reachable Set

First

先考虑简单的情况 , 如果只有一个点,需要删除多少个点呢?

我们的目标是删除该点能够到达的所有点. 用set存下即可. 如果有很多点也可以当成一个点,去维护set即可.

还有一个问题就是在加入\(x\)时,发现当前到不了,那就输出-1,并存下x,后面再补充.

具体实现见代码

vector<vector<int>> ver;
void solve() {
  int n,m;cin>>n>>m;
  ver.resize(n+1);
  set<int> e;
  for (int i=1;i<=m;++i) {
    int x,y;cin>>x>>y;
    if (x>y) swap(x,y);
    if (x==1) e.insert(y);
    ver[x].push_back(y);
    ver[y].push_back(x);
  }
  cout<<e.size()<<"\n";
  set<int> nd;
  for (int i=2;i<=n;++i) {
    if (e.find(i)!=e.end()) {
      e.erase(i);
      queue<int> q;
      q.push(i);
      while (q.size()) {
        auto x=q.front();
        q.pop();
        for (auto y:ver[x]) {
          if (nd.count(y)) q.push(y),nd.erase(y);
          if (y>i) e.insert(y);
        }
      }
    }else {
      nd.insert(i);
    }
    if (nd.empty()) {
      cout<<e.size()<<"\n";
    }else {
      cout<<"-1\n";
    }
  }
}

Second

上述过程可以通过并查集优化. 可以将nd转化为联通块的数量 , 我们将e用一个数组bad和一个计数器era代替.

具体地:用cnt计算当前联通块的数量 , bad记录当前应该删除的点 , era当前应该删除的点的数量.

每次加入一个点, 去遍历其边 , 若发现比它小的结点未联通 , 就将其联通 , 同时cnt--

若发现其连接了大于它的点,将其标记未坏点, 同时era++

若当前加入的点为一个坏点 那么era--.

具体见代码

void solve() {
  int n,m;cin>>n>>m;
  ver.resize(n+1);
  for (int i=1;i<=n;++i) fa[i]=i;
  for (int i=1;i<=m;++i) {
    int x,y;cin>>x>>y;
    if (x>y) swap(x,y);
    ver[x].push_back(y);
    ver[y].push_back(x);
  }
  vector<bool> bad(n+1);
  int cnt=0,era=0;
  for (int i=1;i<=n;++i) {
    ++cnt;
    if (bad[i]) --era;
    for (auto y:ver[i]) {
      if (y<i) {
        if (find(y)!=find(i)) {
          merge(i,y);
          cnt--;
        }
      }else {
        if (!bad[y]) era++;
        bad[y]=1;
      }
    }
    if (cnt==1) {
      cout<<era<<"\n";
    }else {
      cout<<"-1\n";
    }
  }
}

F - Add One Edge 3

本质是求树上所有点为根的最大深度

First

\(a_i\)为在第一棵树中, 以\(i\)为根的最深深度 , 同理定义\(b_i\).

那么显然原问题就是求

\[\sum_{1\le i\le n}\sum_{1\le j\le m} \max(a_i+b_i+1,mx) \]

其中mx为两棵树直径的较大者.

而第一步要先求出每个点的最深深度 , 用树形dp/换根dp可以求出. 然后求出直径 .

struct Tree {
  vector<vector<int>> ver;
  vector<int> dep,sec,a;
  Tree(int n) {
    ver.resize(n+1);
    dep.resize(n+1);
    sec.resize(n+1);
    a.resize(n+1);
  }

  void addEdge(int x,int y) {
    ver[x].push_back(y);
    ver[y].push_back(x);
  }

  int getlen(int root) {
    function<void(int, int)> dfs1 = [&](int x, int fa) -> void {
      for (auto y : ver[x]) {
        if (y == fa) continue;
        dep[y] = dep[x] + 1;
        dfs1(y, x);
      }
      if (dep[x] > dep[root]) {
        root = x;
      }
    };
    dfs1(root, 0);
    int st = root;
    int n=dep.size();
    dep.assign(n,0);
    dfs1(root, 0);
    int ed = root;
    return dep[root];
  }

  void dfs(int x,int fa) {
    dep[x]=sec[x]=0;
    for (auto y:ver[x]) {
      if (y==fa) continue;
      dfs(y,x);
      int k=dep[y]+1;
      if (dep[x]<k) {
        sec[x]=dep[x];
        dep[x]=k;
      }else if (sec[x]<k) {
        sec[x]=k;
      }
    }
  }
  void work() {
    dfs(1,1);
    function<void(int,int)> exroot=[&](int x,int fa) {
      a[x]=dep[x];
      for (auto y:ver[x]) {
        if (y==fa) continue;
        int lstx=dep[x],lsty=dep[y];
        int lst2=sec[y];
        if (dep[x]==dep[y]+1) dep[x]=sec[x];
        if (dep[x]+1>dep[y]) {
          sec[y]=dep[y];
          dep[y]=dep[x]+1;
        }else if (dep[x]+1>sec[y]) {
          sec[y]=dep[x]+1;
        }
        exroot(y,x);
        dep[x]=lstx;
        dep[y]=lsty;
        sec[y]=lst2;
      }
    };
    exroot(1,1);
  }
   int queryk(int k) {
     return a[k];
   }
};

或者

struct Tree {
   //.....
  void dfs(int x,int fa) {
    dep[x]=sec[x]=0;
    for (auto y:ver[x]) {
      if (y==fa) continue;
      dfs(y,x);
      int k=dep[y]+1;
      if (dep[x]<k) {
        sec[x]=dep[x];
        dep[x]=k;
        son[x]=y;
      }else if (sec[x]<k) {
        sec[x]=k;
      }
    }
  }
  void work() {
    dfs(1,1);
    function<void(int,int)> exroot=[&](int x,int fa) {
      for (auto y:ver[x]) {
        if (y==fa) continue;
        up[y]=up[x]+1;
        if (y==son[x]) up[y]=max(up[x],sec[x])+1;
        else up[y]=max(up[x],dep[x])+1;
        exroot(y,x);
      }
    };
    exroot(1,1);
  }
//....
};

然后我们可以利用二分,直接求出答案

  sort(a.begin()+1,a.end());
  sort(b.begin()+1,b.end());
  for (int i=m;i;--i) s[i]=s[i+1]+b[i];
  int ans=0;
  for (int i = 1; i <= n; ++i){
    int need = mx - a[i] - 1;
    int x = lower_bound(b.begin()+ 1, b.end(), need) - b.begin();
    ans += (x - 1) * mx;
    ans +=  (m - x + 1) * (a[i] + 1) + s[x];
  }

或者间接求出答案

int sum1=0,sum2=0;
  for (int i=1;i<=n;++i) sum1+=a[i];
  for (int i=1;i<=m;++i) sum2+=b[i];
  int ans=n*m+n*sum2+m*sum1;
  for (int i=1;i<=n;++i) cnt[a[i]]++;
  for (int i=1;i<=n;++i) pre[i]=pre[i-1]+i*cnt[i];
  for (int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
  for (int i=1;i<=m;++i) {
    int need=mx-b[i]-1;
    need=min(need,n);
    if (need<0) continue;
    int x=cnt[need];
    ans+=x*mx-(pre[need]+x*(b[i]+1));
  }

这里need=min(need,n);比较坑. 会wa3个点.

Second

但是我们注意到 ,假设st->en为一条直径, 那么最大深度为\(\max(d(i,st),d(i,en))\) ,否则就能找到一条更长的路径.

于是在求直径时可以处理出这两个数组.

  void dfs(int x,int fa) {
    a[x]=max(a[x],dep[x]);
    for (auto y: ver[x]) {
      if (y==fa) continue;
      dep[y]=dep[x]+1;
      dfs(y,x);
    }
  }

  int getlen(int root) {
    function<void(int, int)> dfs1 = [&](int x, int fa) -> void {
      for (auto y : ver[x]) {
        if (y == fa) continue;
        dep[y] = dep[x] + 1;
        dfs1(y, x);
      }
      if (dep[x] > dep[root]) {
        root = x;
      }
    };
    dfs1(root, 0);
    int st = root;
    dep[st]=0,dfs(st,st);
    int n=dep.size();
    dep.assign(n,0);
    dfs1(root, 0);
    int ed = root,d=dep[root];
    dep[ed]=0,dfs(ed,ed);
    return d;
  }
posted @ 2025-04-13 19:45  _lull  阅读(32)  评论(0)    收藏  举报