GMOJ4018Magic
Posted stoorz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GMOJ4018Magic相关的知识,希望对你有一定的参考价值。
题目
题目链接:https://gmoj.net/senior/#main/show/4018
一个环上有标号为(1)到(2n)的顶点,每个顶点连接一条无向边。要求选择(3)条不重复的边在顶点处建熊洞(共(6)个),且每条边的两个顶点的距离相同,询问其方案数。
距离定义为在环上从一个顶点到另一个顶点所经过的最少熊洞数量-1。
思路
(3)条弦在一个圆中的相交情况只有如下五种:
我们发现,满足题目要求的只有2号和5号。而这两种图形的方案都不是很好计算,所以考虑计算出不满足题目条件的图形个数。
假设我们先求出了(l[i],r[i])表示第(i)条弦的左右分别有多少条与它不相交的弦,那么图1的情况就有(sum^{n}_{i=1}l[i] imes r[i])种,而图3和图4都有共同点:3条弦中都有两条弦满足另外两条线分别于它相交和相离,所以方案数就是(frac{(l[i]+r[i]) imes (n-l[i]-r[i]-1)}{2}).
那么现在的问题就是如何求(l[i],r[i]),容易发现,一条弦在另一条弦的左/右边,其实就是判断这两条线所连接的点的大小,分类讨论+二维偏序即可。
时间复杂度(O(nlog n))
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=100010;
int n,l[N],r[N];
ll ans;
struct node
{
int x,y,id;
}a[N];
struct BIT
{
int c[N*2];
void add(int x,int val)
{
for (;x<=n*2;x+=x&-x)
c[x]+=val;
}
int ask(int x)
{
int sum=0;
for (;x;x-=x&-x)
sum+=c[x];
return sum;
}
}bit;
bool cmp1(node x,node y)
{
return x.x<y.x;
}
bool cmp2(node x,node y)
{
return x.y>y.y;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
if (a[i].x>a[i].y) swap(a[i].x,a[i].y);
a[i].id=i;
}
sort(a+1,a+1+n,cmp1);
for (int i=1;i<=n;i++)
{
l[a[i].id]+=bit.ask(a[i].x)+bit.ask(n*2)-bit.ask(a[i].y);
bit.add(a[i].y,1);
}
memset(bit.c,0,sizeof(bit.c));
for (int i=n;i>=1;i--)
{
r[a[i].id]+=bit.ask(a[i].y)-bit.ask(a[i].x);
bit.add(a[i].y,1);
}
memset(bit.c,0,sizeof(bit.c));
sort(a+1,a+1+n,cmp2);
for (int i=1;i<=n;i++)
{
l[a[i].id]+=bit.ask(n*2)-bit.ask(a[i].y);
bit.add(a[i].x,1);
}
ans=1LL*n*(n-1)*(n-2)/6;
for (int i=1;i<=n;i++)
ans-=(1LL*l[i]*r[i]+1LL*(l[i]+r[i])*(n-l[i]-r[i]-1)/2);
printf("%lld",ans);
return 0;
}
以上是关于GMOJ4018Magic的主要内容,如果未能解决你的问题,请参考以下文章