【bzoj1145】【CTSC2008】【图腾】【组合数学+树状数组】
2015-09-08 10:13
399 查看
Description
在完成了古越州圆盘密码的研究之后,考古学家小布来到了南美大陆的西部。相传很久以前在这片土地上生活着两个部落,一个部落崇拜闪电,另一个部落崇拜高山,他们分别用闪电和山峰的形状作为各自部落的图腾。小布的团队在山洞里发现了一幅巨大的壁画,壁画上被标记出了N个点,经测量发现这N个点的水平位置和竖直位置是两两不同的。小布认为这幅壁画所包含的信息仅与这N个点的相对位置有关,因此不妨设坐标分别为(1, y1), (2, y2), ..., (n, yn),其中y1~yn是1~N的一个排列。小布的团队打算研究在这幅壁画中包含着多少个图腾,其中闪电图腾的定义图示如下(图腾的形式只与4个纵坐标值的相对大小排列顺序有关):
崇拜高山的部落有两个氏族,因而山峰图腾有如下两种形式,左边为A类,右边为B类(同样,图腾的形式也都只与4个纵坐标值的大小排列顺序有关):
小布的团队希望知道,这N个点中两个部落图腾数目的差值。因此在本题中,你需要帮助小布的团队编写一个程序,计算闪电图腾数目减去山峰图腾数目的值,由于该值可能绝对值较大,本题中只需输出该值对16777216的余数(注意余数必为正值,例如-1对16777216的余数为16777215)。
Input
第一行包含一个整数N,为点的数目。接下来一行包含N个整数,分别为y1, y2, …, yn。保证y1, y2, …, yn是1~N的一个排列。Output
仅包含一个数,表示闪电图腾数目与山峰图腾数目的差值对16777216的余数。Sample Input
【样例输入一】5
1 5 3 2 4
【样例输入二】
4
1 2 4 3
Sample Output
【样例输出一】0
【样例输出二】
16777215
HINT
样例一中共有1个闪电图腾(1324)和1个B类山峰图腾(1532)。样例二中仅有一个A类山峰图腾(1243),故差值为-1,答案为16777215。【数据规模】对于10%的数据,N ≤ 600;对于40%的数据,N ≤ 5000;对于100%的数据,N ≤ 200000。
题解:我们用****表示一种结构的方案数。
显然题目要求的是 1324-1432-1243=(1*2*-1423)-(14**-1423)-(12**-1234)
=1*2*-14**-12**+1234
=1*2*-(1***-13**)+1234
=1*2*-1***+13**+1234
所以我们只要求出来1*2*,1***,13***,1234的方案数就好了。
我们首先预处理出每个数他左边比他小的有多少右边比他小的数有多少.记为l[i],r[i];
对于1***;
我们枚举1,显然1右边的数都比1大,那么就是从(n-i-r[i])中选3个数。
对于1234;
我们枚举3,
显然3右边的数比3大.方案数为(n-i-r[i]);
3左边的数都比他小,并且需要递减.
所以方案数就是sigma(l[x])(x<i,a[x]<a[i]);
这个东西显然可以用树状数组处理。
对于1*2*;
我们枚举2.
显然2右边的数都比2大,这样的数有(n-i-r[i])个。
2左边的数一个比2大一个比2小。
这个东西直接算不好算,我们考虑补集转换。
我们先把固定1,显然这样的方案数有l[i]*(i-1)个。
这样肯定多算了.
我们先减去两个数都比2小并且1在左边的方案数.即l[i]*(l[i]-1)/2;
我们还要减去1在右边的情况
这样显然是sigma(x)(a[x]<a[i],x<i);这个东西当然也可以用树状数组处理。
对于13**
我们枚举3.
显然在3的右边有一个4,4的方案数显然是(n-i-r[i]);
我们还是考虑补集转换。
我们先固定2,1一定比2小,那么总方案数为sigma(a[x])(a[x]<a[i],x>i);
它也可以用树状数组处理。
这样多算了很多。
我们减去两个数都在右边的情况,即减去r[i]*(r[i]+1)/2;
代码:
#include<cstring> #include<iostream> #include<cstdio> #define N 200010 #define p 16777216 using namespace std; long long c ,l ,r ; int h ,n,t1,t2,t3,t4; int lowbit(int x){return x&(-x);} void updata(int x,int v){for (int i=x;i<=n;i+=lowbit(i)) c[i]+=v;} long long query(int x) { long long ans(0); for (int i=x;i;i-=lowbit(i)) ans+=c[i]; return ans; } int work1()//1*2* { memset(c,0,sizeof(c)); long long ans(0); for(int i=1;i<=n;i++) { (ans+=(l[i]*(i-1)-l[i]*(l[i]-1)/2-query(h[i]))*(n-r[i]-i))%=p; updata(h[i],i); } return ans; } int work2()//1234 { memset(c,0,sizeof(c)); long long ans(0); for(int i=1;i<=n;i++) { (ans+=query(h[i])*(n-i-r[i]))%=p; updata(h[i],l[i]); } return ans; } int work3()//13** { memset(c,0,sizeof(c)); long long ans(0); for(int i=n;i;i--) { (ans+=(query(h[i])-r[i]*(r[i]+1)/2)*(n-r[i]-i))%=p; updata(h[i],h[i]); } return ans; } int work4()//1*** { long long ans(0); for(int i=1;i<=n;i++) { long long temp=n-i-r[i]; if(temp>=3) (ans+=temp*(temp-1)*(temp-2)/6)%=p; } return ans; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++){scanf("%d",&h[i]);l[i]=query(h[i]);r[i]=h[i]-1-l[i];updata(h[i],1);} memset(c,sizeof(c),0); //for (int i=1;i<=n;i++) cout<<r[i]<<endl; t1=work1();t2=work2();t3=work3();t4=work4(); // cout<<t1<<' '<<t2<<' '<<t3<<' '<<t4<<endl; printf("%d",(t1+t2+t3-t4+p)%p); }
相关文章推荐
- KMP字符串匹配算法
- 如何让new操作符不分配内存,只调用构造函数
- eclipse 报错 java compiler level does not match the version of the installed java project
- 教程-Python实例-发送邮件功能
- Ubuntu 14.04 开机手动开启numlock led
- CRM Home Grid StyleSet
- linux设备驱动归纳总结(一):内核的相关基础概念
- mybatis打印不出SQL语句的问题
- 【华为上机试题C++】老师想知道从某某同学当中,分数最高的是多少,现在请你编程模拟老师的询问。当然,老师有时候需要更新某位同学的成绩.
- Linux学习:正则表达式习题
- Python Join Examples
- java中的匿名内部类总结
- matlab中repmat函数的用法
- 寻找第K大的数字
- 判断一个整数是否为2的n次幂(算法)
- Kubernetes集群搭建与测试
- 删除链表结点
- 查询存储过程创建时间
- ios+Appium+Java
- 如何解决MathType公式显示方框