[2009国家集训队]小Z的袜子(hose) 分块做法

2 篇文章 0 订阅
1 篇文章 0 订阅


题目描述: Click here

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1N编号,然后从编号LR(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。



这个oj 不支持cout 。。被坑了一个多点。。==第一次碰到这样的oj。。  

解法应该不难。。就是分块然后进行统计。。因为时限太长。。怎么样都不会超时。。暴力除外。

freq[i] 统计的出现的 >= i次的数目。。

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 55555;
const int Sqrt = 333;
#define ll long long
int n,m;
int a[N];
int freq[N],cnt[N];
int zl[N],rr[N],idx[N];
pair<ll,ll> ans[N];
bool cmp(int x,int y){
	if(zl[x]/Sqrt == zl[y]/Sqrt)
		return rr[x] < rr[y];
	return zl[x] < zl[y];
}
ll gcd(ll a,ll b){
	if(b==0)return a;
	return gcd(b,a%b);
}
pair<ll,ll> query(int l,int r){
	ll ans2 = (long long)(r-l+1)*(r-l)/2;
	ll ans1 = 0,i = 1;
	while(freq[i]>0){
		//printf("freq[%d]=%d\n",i,freq[i]);
		int num = freq[i] - freq[i+1];
		if(num)
			ans1 += (ll)i*(i-1)/2*num;
		i++;
	}
	int gc = gcd(ans2,ans1);
	return make_pair(ans1/gc,ans2/gc);
}
int main(){
	while(scanf("%d %d",&n,&m)!=EOF){
		memset(freq,0,sizeof(freq));
		memset(cnt,0,sizeof(cnt));
		for(int i = 1;i <= n;i++) scanf("%d",a+i);
		for(int i = 1;i <= m;i++) scanf("%d %d",zl+i,rr+i);
		for(int i = 1;i <= m;i++) idx[i] = i;
		sort(idx+1,idx+1+m,cmp);
		int lp = 1, rp = 0;
		for(int i = 1;i <= m;i++){
			int l = zl[idx[i]] , r = rr[idx[i]] ;
			while(rp<r) freq[++cnt[a[++rp]]]++;
		//	printf("ce freq[1]=%d\n",freq[1]);
			while(lp>l) freq[++cnt[a[--lp]]]++;
			while(rp>r) freq[cnt[a[rp--]]--]--;
			while(lp<l) freq[cnt[a[lp++]]--]--;
			ans[idx[i]] = query(l,r);
		} 
		for(int i = 1;i <= m;i++){
			printf("%lld/%lld\n",ans[i].first,ans[i].second);
		}
	}
	return 0;
}


#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const int N = 105555;
int Sqrt = 333;
#define ll long long
int n,m;
int a[N];
int cnt[N];
int zl[N],rr[N],idx[N];
struct node{
	ll first,second;
	node(){}
	node(ll _f,ll _s){ first=_f , second=_s;}

};
node ans[N];
/*
bool cmp(int x,int y){
	if(zl[x]/Sqrt == zl[y]/Sqrt)
		return rr[x] < rr[y];
	return zl[x] < zl[y];
}*/
struct q_node{
	int l,r,id;
	bool operator < (const q_node& tmp) const{
		if(l / Sqrt == tmp.l / Sqrt) return r < tmp.r;
		return l < tmp.l;
	}
}Q[N];
ll gcd(ll a,ll b){
	if(b==0)return a;
	return gcd(b,a%b);
}
int main(){
	while(scanf("%d%d",&n,&m) == 2){
		memset(cnt,0,sizeof(cnt));
		for(int i = 1;i <= n;i++) scanf("%d",a+i);
		for(int i = 1;i <= m;i++) scanf("%d %d",&Q[i].l,&Q[i].r);
		for(int i = 1;i <= m;i++) Q[i].id = i;
		Sqrt = (1.0)*sqrt(n);
		sort(Q+1,Q+1+m);
		int lp = 1, rp = 0;
		ll ans1  ,ans2  , tmp = 0 ;
		for(int i = 1;i <= m;i++){
			int l = Q[i].l , r = Q[i].r;
			while(rp<r) {
				rp++;
				tmp -= (ll)cnt[a[rp]]*cnt[a[rp]];
				cnt[a[rp]]++;
				tmp += (ll)cnt[a[rp]]*cnt[a[rp]];
			}
			while(lp>l) {
				lp--;
				tmp -= (ll)cnt[a[lp]]*cnt[a[lp]];
				cnt[a[lp]]++;
				tmp += (ll)cnt[a[lp]]*cnt[a[lp]];
			}
			while(rp>r) {
				tmp -= (ll)cnt[a[rp]]*cnt[a[rp]];
				cnt[a[rp]]--;
				tmp += (ll)cnt[a[rp]]*cnt[a[rp]];
				rp--;
			}
			while(lp<l) {
				tmp -= (ll)cnt[a[lp]]*cnt[a[lp]];
				cnt[a[lp]]--;
				tmp += (ll)cnt[a[lp]]*cnt[a[lp]];
				lp++;
			}
			ans1 = tmp - (r-l+1); ans2 = (ll)(r-l+1)*(r-l);
			ll gc = gcd(ans2,ans1);
			ans[Q[i].id] = node(ans1/gc , ans2/gc);
		} 
		for(int i = 1;i <= m;i++){
			printf("%lld/%lld\n",ans[i].first,ans[i].second);
			//cout << ans[i].first << "/" << ans[i].second << endl;
		}
	}
	return 0;
}

0 0这是类似思路。不同写法。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值