2020牛客暑期多校训练营(第六场)K-Bag (差分数组 + 思维)

链接: K-Bag

2020牛客暑期多校训练营(第六场)K-Bag (差分数组 + 思维)_第1张图片
题意:
定义 k_bag 数列为 k 个 k的全排列 组成的数列,现给出 一个数列和一个 k ,判断这个数列是否为 k _bag 数列的一个子串。
思路:

  1. 如果最近的两个相同数的距离小于 k ,那么在这个区间内必须有一个位置为一个全排列的开头,就可以给这个区间值全部 +1。
  2. 每出现一种上述情况,就加一种限制 ,如果到最后存在一个位置可以满足所有的限制(即这个位置的值为限制的个数),那么就可以得到答案是 yes。
  3. 如果 这个序列的 第一个区间开头是 x1,那么后面的区间开头一定是 x1+y*k,y是一个整数,那么 我只要把当前开头 % k 就可以得到第一区间的开头,这个值可以加到第一个区间开头。
  4. 这个区间值 +1 的操作可以用差分数组维护 。
  5. 对于一个区间 【l , r】,让 区间 每个值+1 可以vis[l]++, vis[R+1]--,最后得到的前缀和即为当前位置的值 (今天刚学会的新姿势 ,吹爆 佳爷)。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int mod=1e9+7;
int a[maxn],vis[maxn],n,k,num[maxn];
map<int ,int >s;
int T,cnt;
void init(int n){
    cnt = 0;
    s.clear();
    for(int i = 1; i <= n; i++){
        num[i]=vis[i]=0;
    }
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        init(n);
        for(int i = 1; i <= n; i++){
            scanf("%d",&a[i]);
            if(s[a[i]] != 0){
                if(i - s[a[i]] < k ){
                   cnt++;
                   vis[s[a[i]] + 1] ++;
                   vis[i + 1]--;
                }
            }
            s[a[i]] = i; 
        }
        int pre = 0 ,fl = 0;
        for(int i = 1; i <= n; i ++){
            pre += vis[i];
            num[i % k] += pre;
        }
        for(int i = 0; i <= n; i ++){
            if(num[i] == cnt){
               fl=1;
               break;
            }
        }
        if( fl ) printf ("YES\n");
        else printf ("NO\n");
    }
}

思路来源于学长 ,我就是个菜鸡 wuwuwu

你可能感兴趣的:(2020牛客暑期多校训练营(第六场)K-Bag (差分数组 + 思维))