P11390 [COCI 2024/2025 #1] 教师 / Učiteljica

洛谷

20pts

直接枚举左右端点,然后判断答案即可。

35pts

由于只有 \(k\) 个数,那么每一个数都一定会对应一种 \(i\) 的取值,也就意味着满足条件的情况一定是是每个数分别对应一种出现次数。

这样的话可以发现长度已经固定了,长度最长也只有 \(10\),那么直接枚举即可。

70pts

此时我们可以枚举最右端的位置,处理出每一个点对应的上一个值,用线段树保存此时哪些地方作为左端点满足条件即可。

代码(有点长):

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, k, a[100005], cnt[5], t[100005], ans, pre[200005], l[200005];
struct P {
    int mi,cnt;//由于全部都是非负数,可以直接通过0的数量得到有哪些部分可行
    P friend operator+(P a, P b) {
        P c;
        c.mi = min(a.mi, b.mi);
        c.cnt = 0;
        if (c.mi == a.mi)
            c.cnt += a.cnt;
        if (c.mi == b.mi)
            c.cnt += b.cnt;
        return c;
    }
    P friend operator+(P a, int b) { return { a.mi + b, a.cnt }; }
};
struct ST {
    P c[800005];
    int tag[800005];
#define ls p << 1
#define rs p << 1 | 1
    void pushup(int p) { c[p] = c[ls] + c[rs]; }
    void Tag(int p, int v) {
        c[p] = c[p] + v;
        tag[p] += v;
    }
    void pushdown(int p) {
        if (!tag[p])
            return;
        Tag(ls, tag[p]);
        Tag(rs, tag[p]);
        tag[p] = 0;
    }
    void build(int p, int l, int r) {
        if (l == r)
            return void(c[p] = { 0, 1 });
        int mid = l + r >> 1;
        build(ls, l, mid), build(rs, mid + 1, r);
        pushup(p);
    }
    void change(int p, int l, int r, int L, int R, int v) {
        if (l >= L && r <= R) {
            Tag(p, v);
            return;
        }
        pushdown(p);
        int mid = l + r >> 1;
        if (mid >= L)
            change(ls, l, mid, L, R, v);
        if (mid < R)
            change(rs, mid + 1, r, L, R, v);
        pushup(p);
    }
    int query() {
        if (c[1].mi == 0)
            return n - c[1].cnt;
        return n;
    }
} tr;
void solve2() {
    int len = k * (k + 1) / 2, ans = 0;
    for (int i = 1; i <= n - len + 1; i++) {
        for (int i = 1; i <= k; i++) t[i] = 0;
        for (int i = 1; i <= k; i++) cnt[i] = 0;
        for (int j = i; j <= i + len - 1; j++) {
            t[a[j]]++;
        }
        for (int i = 1; i <= k; i++)
            if (t[i] <= k && t[i] >= 1)
                cnt[t[i]] = 1;
        bool f = 1;
        for (int i = 1; i <= k; i++)
            if (!cnt[i])
                f = 0;
        if (f)
            ans++;
    }
    cout << ans;
}
void solve3() {
    for (int i = 1; i <= n; i++) {
        pre[i] = l[a[i]];
        l[a[i]] = i;
    }
    tr.build(1, 1, n);
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        tr.change(1, 1, n, pre[i] + 1, i, 1);
        if (pre[i])
            tr.change(1, 1, n, pre[pre[i]] + 1, pre[i], -1);
        ans += tr.query();
    }
    cout << ans;
}
signed main() {
    cin >> n >> k;
    bool f = 1;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if (a[i] > k)
            f = 0;
    }
    if (f) {
        solve2();
        return 0;
    }
    if (k == 1) {
        solve3();
        return 0;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) t[j] = 0;
        for (int j = 1; j <= k; j++) cnt[j] = 0;
        for (int j = i; j <= n; j++) {
            if (t[a[j]] && t[a[j]] <= k) {
                cnt[t[a[j]]]--;
            }
            t[a[j]]++;
            if (t[a[j]] <= k) {
                cnt[t[a[j]]]++;
            }
            bool f = 1;
            for (int o = 1; o <= k; o++)
                if (!cnt[o])
                    f = 0;
            if (f)
                ans++;
        }
    }
    cout << ans;
    return 0;
}

120pts

\(k=1\) 的基础上进行思考。

此时我们已经有方法处理任意一个 \(k\) 的值的范围了。

但是我们如果想要得到这些取值的交集,直接求解还是比较困难的。

所以我们可以考虑使用容斥来解决。

容斥需要求交集,那么我们可以枚举不同的交的情况。

根据小学奥数的知识,奇数加偶数减,我们先预处理出这种情况的数量的奇偶性,使用二进制枚举,即可得到满足 \(k\) 个不同取值的答案了。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,a[100005],cnt[5],t[100005],ans,pre[100005],l[100005],siz[100005];
struct P{
	int mi,cnt;
	P friend operator+(P a,P b){
		P c;
		c.mi=min(a.mi,b.mi);
		c.cnt=0;
		if(c.mi==a.mi)c.cnt+=a.cnt;
		if(c.mi==b.mi)c.cnt+=b.cnt;
		return c;
	}
	P friend operator+(P a,int b){
		return {a.mi+b,a.cnt};
	}
};
struct ST{
	P c[400005];
	int tag[400005];
	#define ls p<<1
	#define rs p<<1|1
	void pushup(int p){
		c[p]=c[ls]+c[rs];
	}
	void Tag(int p,int v){
		c[p]=c[p]+v;
		tag[p]+=v;
	}
	void pushdown(int p){
		Tag(ls,tag[p]);
		Tag(rs,tag[p]);
		tag[p]=0;
	}
	void build(int p,int l,int r){
		tag[p]=0;
		if(l==r)return void(c[p]={0,1});
		int mid=l+r>>1;
		build(ls,l,mid),build(rs,mid+1,r);
		pushup(p);
	}
	void change(int p,int l,int r,int L,int R,int v){
		if(L>R)return;
		if(l>=L&&r<=R){
			Tag(p,v);
			return;
		}
		pushdown(p);
		int mid=l+r>>1;
		if(mid>=L)change(ls,l,mid,L,R,v);
		if(mid<R)change(rs,mid+1,r,L,R,v);
		pushup(p);
	}
}tr;
signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		pre[i]=l[a[i]];
		l[a[i]]=i;
	}
	int ans=0;
	for(int i=1;i<(1<<k);i++){
		siz[i]=siz[i>>1]+(i&1);
	}
	for(int st=1;st<(1<<k);st++){
		tr.build(1,1,n);
		int s=0;
		for(int i=1;i<=n;i++){
			int tmp=i;
			for(int j=1;j<=k;j++){
				if(!tmp)break;
				if(st&(1<<j-1)){
                    tr.change(1,1,n,pre[tmp]+1,tmp,1);
                    if(pre[tmp])tr.change(1,1,n,pre[pre[tmp]]+1,pre[tmp],-1);
                }
                tmp=pre[tmp];
			}
			int tmp2=n-(tr.c[1].mi==0)*tr.c[1].cnt;
			if(siz[st]%2==0)ans-=tmp2;
			else ans+=tmp2;
		}
	}
	cout<<ans;
	return 0;
}

posted @ 2025-12-06 16:14  huhangqi  阅读(0)  评论(0)    收藏  举报
/*
*/