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;
}

浙公网安备 33010602011771号