USACO 2021 United Cows of Farmer John P 题解
题意:现在有 \(n\) 头牛,每头牛有一个品种 \(b_i\) 。代表队会由长度至少为 \(3\) 的一个区间组成。每个代表队需要选出三名领队,区间两端的牛必须作为领队,每个领队要求和该代表队中所有牛的品种不同。问有多少种从这 \(n\) 头牛中选出一个代表队的方案。 如果两个代表队拥有不同的成员或不同的领队,则被认为是不同的。 \(n\leq 2\times 10^5\)
区间问题固定套路:枚举右端点,快速计算左端点的贡献。
记录 \(pre_x\) 为到当前位置品种为 \(x\) 的牛的最近一次出现位置,显然当我们选择第 \(i\) 头牛作为右端点时,只有 \([pre_{b_{i}}+1, i-1]\) 可以作为左端点。
考虑新加入一个品种为 \(x\) 的牛会造成的贡献(左端点)。 当加入一个 \(x\) 时,会对 \(pre_x+1,i-1\) 这个区域内的左端点方案数+1,(这个左端点可以选取 \(x\) 作为中间点)。然后每次查询区间和加和即可。
#include<bits/stdc++.h>
using namespace std;
int read()
{
int ret=0, f=1; char x=getchar();
while(!isdigit(x)) { if(x=='-') f=-1; x=getchar(); }
while(isdigit(x)) { ret=ret*10+x-'0'; x=getchar(); }
return ret*f;
}
const int maxn=2e6+3;
struct SegmenTree{
int left,right;
int ans, val, siz, tag; //
// ans -> the ans of the sum of this segment
// siz -> the sum of the cows
// val -> real numbers
};SegmenTree tree[maxn<<2];
void build(int index,int l,int r)
{
tree[index].left=l; tree[index].right=r;
if(l==r) { return ; }
int mid=(l+r)>>1;
build(index<<1,l,mid); build(index<<1|1,mid+1,r);
}
void pushup(int index)
{
tree[index].ans=tree[index<<1].ans+tree[index<<1|1].ans; tree[index].siz=tree[index<<1].siz+tree[index<<1|1].siz;
}
void pushdown(int index)
{
if(tree[index].tag)
{
tree[index<<1].val+=tree[index].tag;
tree[index<<1|1].val+=tree[index].tag;
tree[index<<1].ans+=tree[index].tag*tree[index<<1].siz;
tree[index<<1|1].ans+=tree[index].tag*tree[index<<1|1].siz;
tree[index<<1].tag+=tree[index].tag; tree[index<<1|1].tag+=tree[index].tag;
tree[index].tag=0;
}
}
void Modify_siz(int index,int pos,int k)
{
if(tree[index].left==tree[index].right)
{
tree[index].siz+=k; tree[index].ans=tree[index].siz*tree[index].val; return ;
}
pushdown(index); int mid=(tree[index].left+tree[index].right)>>1;
if(pos<=mid) { Modify_siz(index<<1,pos,k); }
else { Modify_siz(index<<1|1,pos,k); }
pushup(index);
}
void Modify_num(int index,int l,int r,int k)
{
if(l<=tree[index].left&&r>=tree[index].right)
{
tree[index].tag+=k; tree[index].val+=k;
tree[index].ans+=k*tree[index].siz;
return;
}
pushdown(index);
int mid=(tree[index].left+tree[index].right)>>1;
if(l<=mid) { Modify_num(index<<1,l,r,k); }
if(r>mid) { Modify_num(index<<1|1,l,r,k); }
pushup(index);
}
int Query(int index,int l,int r)
{
if(l<=tree[index].left&&r>=tree[index].right)
{
return tree[index].ans;
}
pushdown(index); int mid=(tree[index].left+tree[index].right)>>1, ret=0;
if(l<=mid) { ret+=Query(index<<1,l,r); }
if(r>mid) { ret+=Query(index<<1|1,l,r); }
return ret;
}
int b[maxn];
int pre[maxn],ppre[maxn];
long long ans;
int main()
{
int n=read();
for(int i=1;i<=n;i++) { b[i]=read(); }
build(1,1,n);
for(int i=1;i<=n;i++)
{
int x=b[i];
if(pre[x]) { Modify_siz(1,pre[x],-1), Modify_num(1,ppre[x]+1,pre[x]-1,-1); }
ans+=Query(1,pre[x]+1,i-1);
Modify_num(1,pre[x]+1,i-1,1);
Modify_siz(1,i,1);
ppre[x]=pre[x]; pre[x]=i;
}
cout << ans << endl;
}
浙公网安备 33010602011771号