P1494 [國家集訓隊]小Z的襪子
阿新 • • 發佈:2021-08-08
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;
}