ps:这场的题解非常nice啊,还提供了各种插图,在理解的过程中感受了出题人思维的强大啊
非常非常非常非常非常难的比赛,反正还是由于我太弱了。。。
A:略
B:略
C : 构造题,看到下面这张图片就懂了。。。。
深蓝色的部分是unique的,大红色的部分也是unique的,各占据了三分之二。。。。。
D:构造题
如果k=1,那么就是无脑判断了。。。。
如果k>=2,一定有解,我们就假设k=2.
如果h<w我们可以先满足每一行,放好当前行后,与上一行比较,如果非法的情况超过了一半,就将当前行的两种颜色互换(取反),这样的话当前行还是全部满足条件,与上一行之间的合法数量也>=一半,因此,这样子一行行下来的总合法量肯定超过了3/4,神构造啊,,,,,,
E: 官方题解
计数题,突破口在于三条线的相交方式只有五种,然后我们要计算其中的两种的总数,但是由于直接算比较难,于是可以反过来求
即求C(n,3)减去川字型,减去XD形状的,再减去H形状的,主要就是要算两个东东。
1:一条直线内有几条线段,这个可以用树状数组来求,每次遍历到一个线段的右端点就把左端点插进来(先求和再插),划一划就明白了。
2:求一条线与几条线相交,这个可以根据上面的来求,因为减去在内部的线段*2之后的点都是在这条线内的孤立的点的个数,,,自然是相交的
其他的看看代码就能想明白了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX_N = 222222;
int c[MAX_N];
void update(int x)
{
for(;x<MAX_N;x+=x&-x) c[x]++;
}
int sum(int x)
{
int ans = 0;
for(;x;x-=x&-x) ans += c[x];
return ans;
}
int a[MAX_N] , b[MAX_N] , p[MAX_N] , in[MAX_N] , id[MAX_N] ,inter[MAX_N];
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> a[i] >> b[i];
if(a[i] > b[i]) swap(a[i],b[i]);
id[a[i]] = id[b[i]] = i;
p[a[i]] = b[i];
p[b[i]] = a[i];
}
int m = 2 * n;
for(int i = 1; i <= m; i++)
{
if(p[i] < i)
{
in[id[i]] = sum(i) - sum(p[i]-1);
update(p[i]);
}
}
for(int i = 1; i <= n; i++)
{
inter[i] = b[i] - a[i] - 1 - 2 * in[i];
}
// for(int i = 1; i <= n; i++) printf("%d ",inter[i]);
long long ans = (long long)n*(n-1)*(n-2) / 6;
long long chuang = 0 , cha = 0;
for(int i = 1; i <= n; i++)
{
cha += (long long)inter[i] * (n-1-inter[i]);
chuang+= (long long)in[i] * (n-1-in[i]-inter[i]);
}
cout<<ans-cha/2-chuang<<endl;
return 0;
}
summary : 好强大的一场题目啊,这种题是怎么被想出来的,我这么挫该如何拯救!!!!