1. 程式人生 > 其它 >P1494 [國家集訓隊]小Z的襪子

P1494 [國家集訓隊]小Z的襪子

P1494 [國家集訓隊]小Z的襪子

普通莫隊模板題。

考慮對於任意一個區間,只需要維護平方和即可(推導題解裡面就有)

中間這一段核心操作解釋以下:

for(register int i=1,l=1,r=0;i<=m;++i){
        for(;r<e[i].r;++r)
            update(r+1,1);
        for(;r>e[i].r;--r)
            update(r,-1);
        for(;l<e[i].l;++l)
            update(l,-1);
        for(;l>e[i].l;--l)
            update(l-1,1);
        if(e[i].l==e[i].r){
            e[i].a=0,e[i].b=1;
            continue;
        }
        e[i].a=ans-(e[i].r-e[i].l+1);
        e[i].b=(e[i].r-e[i].l+1)*1LL*(e[i].r-e[i].l);
        ll g=gcd(e[i].a,e[i].b);
        e[i].a/=g;
        e[i].b/=g;
    }

將這一段程式碼分開來看。

for(;r<e[i].r;++r)
	update(r+1,1);
  • 將區間右端點少了的東西補上。
for(;r>e[i].r;--r)
	update(r,-1);
  • 將區間右端點多餘的東西刪去
for(;l<e[i].l;++l)
	update(l,-1);
  • 將左端點多餘的東西先刪去
for(;l>e[i].l;--l)
	update(l-1,1);
  • 再補上左端點少了的東西

這樣反覆推敲肯定不會有問題啦!

這裡,我將這四段程式碼的順序調為如下格式,一樣能AC:

for(;r>e[i].r;--r)
	update(r,-1);
for(;r<e[i].r;++r)
	update(r+1,1);        
for(;l>e[i].l;--l)
	update(l-1,1);
for(;l<e[i].l;++l)
   update(l,-1);

說明順序並沒有任何影響。


update

1)具體\(update\)裡面怎麼寫還是看題目為標準

2)這道題目主要是在維護區間的平方

inline void update(ll x,ll val){
    ans-=s[c[x]]*s[c[x]];
    s[c[x]]+=val;
    ans+=s[c[x]]*s[c[x]];
}

3)後面就直接跟著式子打就好了。


注意事項

1)這題目的排序有毒!嘗試過以下情況:

inline int cmp(Node a,Node b){
    return a.pos!=b.pos?a.l<b.l:((a.pos&1)?a.r<b.r:a.r>b.r);
}

inline int cmp(Node at,Node az){    if(pos[at.pos]==pos[az.pos])        return at.r<az.r;    return at.l<az.l;}

(當然後面這個顯然不對了)

至今原因不明。

#include <bits/stdc++.h>
#define ll long long
#define maxn 50005
using namespace std;
ll n,m,c[maxn],s[maxn];
ll lg,ans,pos[maxn];
struct Node{
    ll l,r,pos;
    ll a,b;
}e[maxn<<2];

inline ll read(){
    ll x=0,f=0;char c=getchar();
    while(!isdigit(c))
        f|=c=='-',c=getchar();
    while(isdigit(c))
        x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return f?-x:x;
}

inline int cmp(Node at,Node az){
    if(pos[at.l]==pos[az.l])
        return at.r<az.r;
    return at.l<az.l;
}

inline int cmp_two(Node at,Node az){
    return at.pos<az.pos;
}

inline ll gcd(ll a,ll b){
    return (b==0)?a:gcd(b,a%b);
}

inline void update(ll x,ll val){
    ans-=s[c[x]]*s[c[x]];
    s[c[x]]+=val;
    ans+=s[c[x]]*s[c[x]];
}

inline void solve(){
    for(register int i=1,l=1,r=0;i<=m;++i){
        for(;r>e[i].r;--r)
            update(r,-1);
        for(;r<e[i].r;++r)
            update(r+1,1);
        
        
        for(;l>e[i].l;--l)
            update(l-1,1);
        for(;l<e[i].l;++l)
            update(l,-1);    
        if(e[i].l==e[i].r){
            e[i].a=0,e[i].b=1;
            continue;
        }
        e[i].a=ans-(e[i].r-e[i].l+1);
        e[i].b=(e[i].r-e[i].l+1)*1LL*(e[i].r-e[i].l);
        ll g=gcd(e[i].a,e[i].b);
        e[i].a/=g;
        e[i].b/=g;
    }
}

int main(){
    n=read();m=read();
    for(register int i=1;i<=n;++i)
        c[i]=read();
    lg=sqrt(n);
    for(register int i=1;i<=n;++i)
        pos[i]=(i-1)/lg+1;
    for(register int i=1;i<=m;++i)
        e[i].l=read(),e[i].r=read(),e[i].pos=i;
    sort(e+1,e+m+1,cmp);
    solve();
    sort(e+1,e+m+1,cmp_two);
    for(register int i=1;i<=m;++i)
        printf("%lld/%lld\n",e[i].a,e[i].b);
    return 0;
}