4.4 相交弧 容斥 平衡规划 二维数点
看起来是一个相当经典的问题 不过很难解决。
考虑我们只需要统计两个圆弧相交的情况 所以对于前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;
}