bzoj2038[2009国家集训队]小Z的袜子(hose)
题意:
把N只袜子从1到N编号,每次求从编号为L到R的袜子中抽两只,有多大的概率抽到颜色相同的袜子。
题解:
不知道要用什么数据结构,但是可以用一个全局的数组保存每个颜色当前数量,使由区间[l,r]推出[l,r±1]的答案和[l±1,r]的复杂度为O(1),对这种问题,可以用复杂度为O(nsqrt(n))的莫队算法解决。
莫队算法是一种离线算法,将询问按某种顺序排序,使得均摊复杂度为O(nsqrt(n)),那怎么排序呢?如果按左端点排序,那么r将有可能多次大幅度摆动,使复杂度退化成O(n^2),正解是对端点分块,让后按左端点所在块为第一关键字排序,右端点为第二关键字排序。这样子当两个询问l在同一块时,l只有可能移sqrt(n)次。l在同一块的多次询问q只能右移n次,l在不同块时r可能左移n次,但因为只有sqrt(n)块,所以需移n次的操作都只有sqrt(n)次,因此均摊复杂度是O(sqrt(n))。所有的均摊复杂度都是玄学……
因为中间结果没有强制转化成long long,wa了好几发,本弱太弱了!
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 #define ll long long 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 using namespace std; 8 9 struct nd1{ 10 int l,pl,r,id; ll ans; 11 }; 12 bool cmp1(nd1 a,nd1 b){ 13 if(a.pl!=b.pl)return a.pl<b.pl; if(a.r!=b.r)return a.r<b.r; 14 return a.l<b.l; 15 } 16 bool cmp2(nd1 a,nd1 b){ 17 return a.id<b.id; 18 } 19 nd1 a1[100000];int col[100000],pos[100000],n,m,l,r;ll ans,s[100000]; 20 inline void update(int x,int y){ 21 ans-=(s[col[x]]*(s[col[x]]-1));s[col[x]]+=(ll)y;ans+=(s[col[x]]*(s[col[x]]-1)); 22 } 23 ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);} 24 int main(){ 25 scanf("%d%d",&n,&m); inc(i,1,n)scanf("%d",&col[i]); int sz=(int)sqrt(n); 26 inc(i,1,n)pos[i]=(i-1)/sz+1; 27 inc(i,1,m){ 28 int a,b;scanf("%d%d",&a,&b);a1[i]=(nd1){a,pos[a],b,i,0}; 29 } 30 sort(a1+1,a1+1+m,cmp1); l=1; r=0; ans=0; memset(s,0,sizeof(s)); 31 inc(i,1,m){ 32 while(r<a1[i].r)update(r+1,1),r++; 33 while(l>a1[i].l)update(l-1,1),l--; 34 while(r>a1[i].r)update(r,-1),r--; 35 while(l<a1[i].l)update(l,-1),l++; 36 a1[i].ans=ans; 37 } 38 sort(a1+1,a1+1+m,cmp2); 39 inc(i,1,m){ 40 if(a1[i].ans==0)printf("0/1 ");else{ 41 ll a2=gcd(a1[i].ans,(ll)(a1[i].r-a1[i].l+1)*(a1[i].r-a1[i].l)); 42 printf("%lld/%lld ",a1[i].ans/a2,(ll)(a1[i].r-a1[i].l+1)*(a1[i].r-a1[i].l)/a2); 43 } 44 } 45 return 0; 46 }
20160405