4.4 相交弧 容斥 平衡规划 二维数点

avatar
avatar

看起来是一个相当经典的问题 不过很难解决。

考虑我们只需要统计两个圆弧相交的情况 所以对于前70分 可以直接把所有的圆弧求出来。

对于n<=1000 显然圆弧最多n^2个 对于颜色数<=20 显然圆弧最多1e6个.

之后得到了若干个二元组 (x,y) 我们想求出 对于不同的颜色的二元组 x<xx<y<yy的数量.

考虑排序后树状数组 在对于x 让所有xx>x在yy处加1 然后y+1到n区间求和。

可以发现 xx可能>y所以考虑减掉不合法的 再开一个树状数组 将xx处加1 再减去y+1到n的xx的和即可。

可以发现还是有不合法的情况 就是在颜色上的问题 考虑预处理的时候对于同种颜色将上述满足条件的去掉即可。

期望得分70.

const int N=1000010,MAXN=100010;
int n,maxx,cnt;ll ans;
int a[MAXN],c[MAXN],b[MAXN];
vector<int>g[MAXN];
struct wy{int x,y;}t[N];
inline int cmp(wy a,wy b){return a.x==b.x?a.y<b.y:a.x<b.x;}
inline void add(int x,int y){while(x<=n){c[x]+=y;x+=x&(-x);}}
inline void add1(int x,int y){while(x<=n){b[x]+=y;x+=x&(-x);}}
inline int ask(int x){int cnt=0;while(x){cnt+=c[x];x-=x&(-x);}return cnt;}
inline int ask1(int x){int cnt=0;while(x){cnt+=b[x];x-=x&(-x);}return cnt;}
int main()
{
	freopen("arc.in","r",stdin);
	freopen("arc.out","w",stdout);
	get(n);
	rep(1,n,i)get(a[i]),g[a[i]].pb(i),maxx=max(maxx,a[i]);
	rep(1,maxx,i)
		for(ui j=0;j<g[i].size();++j)
			for(ui k=j+1;k<g[i].size();++k)
			{
				ans=(ans-(ll)(k-j)*(g[i].size()-1-k)%mod)%mod;
				t[++cnt]=(wy){g[i][j],g[i][k]};
			}
	sort(t+1,t+1+cnt,cmp);
	rep(1,cnt,i)add(t[i].y,1),add1(t[i].x,1);
	int flag=1;
	rep(1,cnt,i)
	{
		while(t[flag].x<=t[i].x&&flag<=cnt)
		{
			add(t[flag].y,-1);
			add1(t[flag].x,-1);
			++flag;
		}
		//此时c中x>t[i].x
		ans=(ans+(ask(n)-ask(t[i].y)))%mod;
		ans=(ans-(ask1(n)-ask1(t[i].y)))%mod;
	}
	putl((ans%mod+mod)%mod);return 0;
}

考虑100分。
直接容斥 首先求出所有不同颜色的圆弧之间的所有交.

问题是减去AABB型和ABBA型的个数.

先考虑 AABB型 枚举第二个A 预处理出后缀的BB型的个数即可O(n)求出。

再考虑 ABBA型 发现很难搞 考虑平衡规划 设出一个lim 对于CntA>=lim.

可以考虑暴力处理所有的这样的A 对于所有的B枚举他们出现的位置然后利用前后缀的A的个数来判断。

注意要累计前缀的B的A个个数 因为后缀的一个B可以和若干个前缀的B形成环 这个时候前缀的A也要累计。

B的位置枚举可以使用链表 复杂度 n^2/lim.

接下来考虑CntB>=lim 发现对于CntA<lim的也是可以暴力枚举B的。

然后接下来枚举A 如何求答案。发现非常的难求。

考虑容斥。对于当前的右端点i来说 总方案为 pre[i](pre[i]-1)/2(c[i]-1).

不合法方案 两种 一种是B的那些区间全都在j的左侧 一种是一边在左侧一边在右侧。

\(\sum{j}pre[j]*(pre[j]-1)/2+pre[j]\cdot (pre[i]-pre[j])\)

可以发现这个式子可以前缀和维护 这也同时是一个级数的形式。

所以这一步 复杂度仍然为n^2/lim.

最后一种情况 CntB<lim CntA<lim.

我们把所有的A放在一起考虑 可以发现此时是一个二维数点问题 也就是对于二元组 (x,y) 求出所有的 xx<x<y<yy.

这些二元组数量为 nlim 还是考虑在y处插入值然后进行区间查询即可 值得注意的是相同颜色的二元组不要重复统计。

这一步的复杂度为 nlim^2log.

发现当lim取 n/logn开三次方比较优。lim->300左右就行。

一道分类讨论难题。让我明白了 容斥可以套容斥再套容斥套一堆东西。啥东西都可以利用容斥。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 10000000000000000ll
#define ld long double
#define pb push_back
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define gc(a) scanf("%s",a+1);
#define rep(p,n,i) for(RE ll i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define pii pair<int,int> 
#define F first
#define S second
#define mk make_pair
#define P 13331ll
#define mod 1000000007
#define RE register
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define ull unsigned long long
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
	return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
	RE int x=0,f=1;char ch=getc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
	return x*f;
}
const int MAXN=100010,lim=300;
int n,m,num;ll ans;
int a[MAXN],b[MAXN];ll C[MAXN],hz[MAXN],qz[MAXN],c[MAXN];
int fir[MAXN],nex[MAXN],las[MAXN],w[MAXN];
inline void discrete()
{
	sort(a+1,a+1+n);
	rep(1,n,i)if(i==1||a[i]!=a[i-1])a[++m]=a[i];
	rep(1,n,i)b[i]=lower_bound(a+1,a+1+m,b[i])-a;
}
inline void prepare()
{
	rep(1,n,i)
	{
		if(!c[b[i]])fir[b[i]]=i;
		else
		{
			nex[c[b[i]]]=i;
		//	if(nex[c[b[i]]]<c[b[i]])cout<<"ww"<<endl;
			las[i]=c[b[i]];
		}
		c[b[i]]=i;++w[b[i]];
		C[i]=(ll)i*(i-1)/2;
	}
	ll now=0;
	rep(1,m,i)
	{
		ans=(ans+now*C[w[i]])%mod;
		now=(now+C[w[i]])%mod;
	}
}
inline void solve_AABB()
{
	rep(1,m,i)c[i]=0;
	fep(n,1,i)
	{
		++c[b[i]];a[i]=0;
		hz[i]=c[b[i]]-1+hz[i+1];
	}
	ll cnt=0;
	rep(1,n,i)
	{
		++a[b[i]];
		ll ww=a[b[i]]-1;
		cnt=(cnt+(hz[i+1]-C[w[b[i]]-a[b[i]]])*ww)%mod;
	}
	ans=(ans-cnt)%mod;
}
inline void solve_A(int x)
{
	rep(1,n,i)qz[i]=qz[i-1]+(b[i]==x);
	fep(n,1,i)hz[i]=hz[i+1]+(b[i]==x);
	ll cnt=0;
	rep(1,m,i)
	{
		if(x==i)continue;
		ll now=0;
		for(int j=fir[i];j;j=nex[j])
		{
			cnt=(cnt+now*hz[j+1])%mod;
			now=(now+qz[j])%mod;
		}
	}
	ans=(ans-cnt)%mod;
}
inline void solve_B(int x)
{
	rep(1,n,i)qz[i]=qz[i-1]+(b[i]==x);
	ll s1,s2,s3,ss,cc,cnt=0,sum=0;
	rep(1,m,i)
	{
		if(w[i]>=lim)continue;
		s1=s2=s3=sum=ss=cc=0;
		for(int j=fir[i],k=0;j;j=nex[j],++k)
		{
			sum=(sum+C[qz[j]]*k)%mod;
			if(las[j])
			{
				ss=(ss+C[qz[las[j]]])%mod;
				s1=(s1+ss)%mod;
				s2=(s2+qz[las[j]])%mod;
				sum=(sum-s2*qz[j])%mod;
				cc=(cc+qz[las[j]]*qz[las[j]])%mod;
				s3=(s3+cc)%mod;
			}
		}
		sum=(sum-s1+s3)%mod;cnt=(cnt+sum)%mod;
	}
	ans=(ans-cnt)%mod;
}
inline void add(int x,int y){while(x<=n){c[x]=c[x]+y;x+=x&(-x);}}
inline ll ask(int x){ll cnt=0;while(x){cnt=cnt+c[x];x-=x&(-x);}return cnt%mod;}
inline void solve_C()
{
	rep(1,n,i)c[i]=0;
	rep(1,n,i)if(w[b[i]]<lim)
	for(int j=i;j;j=nex[j])
		if(nex[j])add(nex[j],1);
	ll cnt=0;
	rep(1,n,i)
	{
		if(w[b[i]]>=lim)continue;
		for(int j=i;j;j=nex[j])
			if(nex[j])add(nex[j],-1);
		int ww=1;
		for(int j=i;j;j=nex[j])
		{
			if(nex[j])
			{
				++ww;
				cnt=(cnt+ask(nex[j]-1)-ask(i)-C[ww-2])%mod;
			}
		}
	}
	ans=(ans-cnt)%mod;
}
inline void solve_ABBA()
{
	//put(lim);
	int ww=0;
	rep(1,m,i)
	{
		if(w[i]<lim)continue;
		solve_A(i);
		solve_B(i);
	}
	//putl((ans+mod)%mod);
	solve_C();
}
int main()
{
	freopen("arc.in","r",stdin);
	freopen("arc.out","w",stdout);
	get(n);rep(1,n,i)b[i]=get(a[i]);
	discrete();prepare();solve_AABB();//putl(ans);
	solve_ABBA();putl((ans+mod)%mod);return 0;
}

猜你喜欢

转载自www.cnblogs.com/chdy/p/12635065.html