K-Bag

K-Bag

https://ac.nowcoder.com/acm/contest/5671/K

题目大题:K—Bag:数组是由若干个长度为k的全排列组成的,然后在开头和结尾都可以删去位置连续的若干个元素。
最后要你判断是否是K-Bag

我们定义一种边(u, v);
若u=1 或v=n+1,则要求a[u],a[u+1],..,a[v-1]都是由不同的数字组成的
反之,我们要求 v-u == k, 并且要求a[u],a[u+1],..,a[v-1]恰好是一个全排列

至于为什么要这样定义。仔细想想就好了,每个边其实都代表下标[u,v)之间其实都是不悖于条件的,要么是完整的一个全排列,要么可能是被删去了一部分的全排列。

最后我们判断是否可以由1到达n+1, 若可以则输出YES,反之NO。

注意实现的细节。对于u=1 或 v=n+1, 我们可以特判,由于数字可能会很大,我们可以用set

  unordered_set<int> se;
  for(int i; i <= n; i++) {
    if(a[i] > k) break;
    if(se.find(a[i]) != se.end()) break;
    add(1, i+1);
    se.insert(a[i]);
  } 
  se.clear();
  for(int j = n; j >= 1; j--) {
    if(a[i] > k) break;
    if(se.find(a[j]) != se.end()) break;
    add(j, n+1);
    se.insert(a[j]);
  } 

然后对于另外一种边,我们可以用滑动窗口,至于为什么终点要多加一。自然是为了能让图连上啦。
vi数组用于判断是否出现。当然我们也可以发现只有n<=k的时侯才会出现这种边

int l = 1, cnt = 0;
  if(k <= n)
  for(int i = 1; i <= n; i++) {
    if(a[i] > k) break;
    if(vi[a[i]]) {
      while(l < i && a[l] != a[i]) vi[a[l++]] = 0, cnt--;
      if(l < i) l++;
      if(cnt == k) add(l, i+1);
    } 
    else { 
      cnt++;
      vi[a[i]] = 1;
      if(cnt == k) add(l, i+1);
    }  
  }

然后我们就可以开开心心的用bfs来判断是否可以到达n+1。
我的渣渣代码。
注意初始化!!!

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
vector<int> v[500005];
bool vis[500005];
queue<int> q;
int p, n, a[500005];
void add(int l, int r) {
  v[l].push_back(r);
}
bool dij() {
  while(!q.empty()) q.pop();
  q.push(1);
  while (!q.empty()) {
    p = q.front();
    q.pop();
    if(vis[n+1]) break;
    if(vis[p]) continue;
    vis[p] = 1;
    for(auto it : v[p]) {
      if(!vis[it])
      q.push(it);
    }
  }
  return vis[n+1] == 1;
}
bool vi[500005];
void init() {
  int k; read(n); read(k);
  _rep(1, n, i) read(a[i]), vis[i] = 0, v[i].clear(), vi[i] = 0;
  v[n+1].clear(); vis[n+1] = 0;
  unordered_set<int> se;
  for(int i; i <= n; i++) {
    if(a[i] > k) break;
    if(se.find(a[i]) != se.end()) break;
    add(1, i+1);
    se.insert(a[i]);
  }
  se.clear();
  for(int j = n; j >= 1; j--) {
    if(a[j] > k) break;
    if(se.find(a[j]) != se.end()) break;
    add(j, n+1);
    se.insert(a[j]);
  }
  se.clear();
  int l = 1, cnt = 0;
  if(k <= n)
  for(int i = 1; i <= n; i++) {
    if(a[i] > k) break;
    if(vi[a[i]]) {
      while(l < i && a[l] != a[i]) vi[a[l++]] = 0, cnt--;
      if(l < i) l++;
      if(cnt == k) add(l, i+1);
    }
    else {
      cnt++;
      vi[a[i]] = 1;
      if(cnt == k) add(l, i+1);
    } 
  }
}
int main() {
  int t; read(t);
  while(t--) {
    init();
    if(dij()) cout << "YES" << endl;
    else cout << "NO" << endl;
  }
}
全部评论
2 4 5 1 2 1 2 4 5 1 2 1 2 老哥你这样例不对
点赞 回复 分享
发布于 2020-07-28 09:03

相关推荐

球球别再泡了:坏,我单9要了14
点赞 评论 收藏
分享
3 收藏 评论
分享
牛客网
牛客企业服务