题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5419
题意:有n个礼物,1~n。m个区间,第i个为li~ri。每次随机三个区间,取走 no less than max(li,lj,lk) and no larger than间的礼物,问取走礼物个数的期望
解法一:线段树区间更新,对大于三的点C(n,3),后求和。值为n说明有n官网区间可以覆盖这个点,从中选三个。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
#define mem(a,b) memset((a),(b),sizeof((a)))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define root 1 , n , 1
#define ll long long
void RI (int& x){
x = 0;
char c = getchar ();
while (c == ' '||c == '\n') c = getchar ();
bool flag = 1;
if (c == '-'){
flag = 0;
c = getchar ();
}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';
c = getchar ();
}
if (!flag) x = -x;
}
void RII (int& x, int& y){RI (x), RI (y);}
void RIII (int& x, int& y, int& z){RI (x), RI (y), RI (z);}
const int maxn = 50010;
ll add[maxn<<2];
ll sum[maxn<<2];
void PushUp(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void PushDown(int rt,int m) {
if (add[rt]) {
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];
sum[rt<<1] += add[rt] * (m - (m >> 1));
sum[rt<<1|1] += add[rt] * (m >> 1);
add[rt] = 0;
}
}
void update(int L,int R,int c,int l,int r,int rt) {
if (L <= l && r <= R) {
add[rt] += c;
sum[rt] += (ll)c * (r - l + 1);
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , c , lson);
if (m < R) update(L , R , c , rson);
PushUp(rt);
}
ll query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
ll ret = 0;
if (L <= m) ret += query(L , R , lson);
if (m < R) ret += query(L , R , rson);
return ret;
}
int mon[maxn];
ll gcd(ll a, ll b){
return b==0?a:gcd(b,a%b);
}
int main() {
//freopen("test.txt","r",stdin);
int T;
RI(T);
while(T --){
int n,m;
RII(n,m);
mem(sum,0);mem(add,0);
for(int i = 1;i <= n;i ++){
RI(mon[i]);
}
for(int i = 0;i < m;i ++){
int l,r;
RII(l,r);
update(l,r,1,1,n,1);
}
ll ans = 0;
for(int i = 1;i <= n;i ++){
ll nn = query(i,i,1,n,1);//cout<<nn<<' ';
if(nn >= 3)ans += (ll)mon[i]*nn*(nn-1)*(nn-2)/6;
}
if(m <= 2){cout<<0<<endl;continue;}
ll mu = (ll)m*(m-1)*(m-2)/6;
if(ans == 0){cout<<0<<endl;continue;}
ll gg = gcd(ans,mu);
ans /= gg;mu /= gg;
if(mu == 1){
cout<<ans<<endl;
}
else printf("%I64d/%I64d\n",ans,mu);
}
return 0;
}
不用线段树,对于每个区间,起点加一,终点减一。具体见代码。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
#define mem(a,b) memset((a),(b),sizeof((a)))
#define ll long long
void RI (int& x){
x = 0;
char c = getchar ();
while (c == ' '||c == '\n') c = getchar ();
bool flag = 1;
if (c == '-'){
flag = 0;
c = getchar ();
}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';
c = getchar ();
}
if (!flag) x = -x;
}
void RII (int& x, int& y){RI (x), RI (y);}
void RIII (int& x, int& y, int& z){RI (x), RI (y), RI (z);}
const int maxn = 50010;
int mon[maxn];
int num[maxn];
int main() {
//freopen("test.txt","r",stdin);
int T;
RI(T);
while(T --){
int n,m;
RII(n,m);
mem(num,0);
for(int i = 1;i <= n;i ++){
RI(mon[i]);
}
for(int i = 0;i < m;i ++){
int l,r;
RII(l,r);
num[l] ++;num[r+1] --;
}
ll ans = 0;
int tmp = 0;
for(int i = 1;i <= n;i ++){
tmp += num[i];//cout<<tmp<<' ';
if(tmp >= 3)ans += (ll)mon[i]*tmp*(tmp-1)*(tmp-2)/6;
}
if(m <= 2){cout<<0<<endl;continue;}
ll mu = (ll)m*(m-1)*(m-2)/6;
if(ans == 0){cout<<0<<endl;continue;}
ll gg = __gcd(ans,mu);
ans /= gg;mu /= gg;
if(mu == 1){
cout<<ans<<endl;
}
else printf("%I64d/%I64d\n",ans,mu);
}
return 0;
}