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;
}
posted @ 2021-05-17 16:19  Van-Helsing  阅读(256)  评论(0)    收藏  举报