BZOJ2038: [2009国家集训队]小Z的袜子(hose)(洛谷P1494)

292 篇文章 1 订阅
281 篇文章 1 订阅

莫队

BZOJ题目传送门
洛谷题目传送门

莫队板子题。。。至于怎么维护答案,让我们推一推式子:

t[i] t [ i ] 表示上一次询问中颜色为 i i 的袜子数量。

Cnm=n!m!(nm)!
ans=ni=1C2t[i]C2rl+1 a n s = ∑ i = 1 n C t [ i ] 2 C r − l + 1 2

上面 =ni=1t[i]!2(t[i2])!=ni=1t[i](t[i]1)2 = ∑ i = 1 n t [ i ] ! 2 ( t [ i − 2 ] ) ! = ∑ i = 1 n t [ i ] ( t [ i ] − 1 ) 2

下面 =(rl+1)!2(rl1)!=(rl+1)(rl)2 = ( r − l + 1 ) ! 2 ( r − l − 1 ) ! = ( r − l + 1 ) ( r − l ) 2

约分展开得

ans=ni=1t[i]2ni=1t[i](rl+1)(rl)=ni=1t[i]2(rl+1)(rl+1)(rl) a n s = ∑ i = 1 n t [ i ] 2 − ∑ i = 1 n t [ i ] ( r − l + 1 ) ( r − l ) = ∑ i = 1 n t [ i ] 2 − ( r − l + 1 ) ( r − l + 1 ) ( r − l )

然后我们只要算 ni=1t[i] ∑ i = 1 n t [ i ] 就可以啦!

代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 50000
using namespace std;
typedef long long LL;
struct qstn{
    int l,r,id,kl,kr;
}que[MAXN+5];
int n,m;
LL sum;
int a[MAXN+5],t[MAXN+5];
LL ans[MAXN+5][2];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline int _read(){
    int num=0; char ch=readc();
    while (ch<'0'||ch>'9') ch=readc();
    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
    return num;
}
bool cmp(qstn x,qstn y){
    return (x.kl<y.kl||(x.kl==y.kl&&x.kr<y.kr));//按照块的编号排
}
void nsrt(int l,int r,int v){
    for (int i=l;i<=r;i++){//按照那样算过去
        sum-=(LL)t[a[i]]*t[a[i]];
        t[a[i]]+=v;
        sum+=(LL)t[a[i]]*t[a[i]];
    }
}
LL gcd(LL a,LL b){
    if (b==0) return a;
    return gcd(b,a%b);
}
int main(){
    n=_read(),m=_read(); int kk=sqrt(n);
    for (int i=1;i<=n;i++) a[i]=_read();
    for (int i=1;i<=m;i++){
        que[i].id=i,que[i].l=_read(),que[i].r=_read();
        que[i].kl=(que[i].l-1)/kk+1; que[i].kr=(que[i].r-1)/kk+1;//不分块会死人
    }
    sort(que+1,que+m+1,cmp);
    int l=1,r=0,nd;
    for (int i=1;i<=m;i++){
        if (que[i].l==que[i].r){//洛谷上要加上这句话
            ans[que[i].id][1]=1; continue;
        }
        if (que[i].l>l) nsrt(l,que[i].l-1,-1);
        else nsrt(que[i].l,l-1,1);
        if (que[i].r>r) nsrt(r+1,que[i].r,1);
        else nsrt(que[i].r+1,r,-1);
        l=que[i].l,r=que[i].r,nd=que[i].id;
        ans[nd][0]=sum-(r-l+1);
        ans[nd][1]=(LL)(r-l+1)*(r-l);
        LL k=gcd(ans[nd][0],ans[nd][1]);
        ans[nd][0]/=k; ans[nd][1]/=k;
    }
    for (int i=1;i<=m;i++)
        printf("%lld/%lld\n",ans[i][0],ans[i][1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值