小さなZソックス(HYSBZ-2038)
人々の規律の人生として、小さなZ毎朝着用するカラフルな靴下の山から1ペアを見つけるのに長い時間がかかります。最後に1日、小さなZはもはや靴下を見つけるために、この迷惑なプロセスを容認することはできませんので、彼はNに、そして数LからRに1から番号......具体的には、この小さなZのNソックス(L小さなものの辞任を決めましたZは、靴下のペアが完了していない2気にしない、でも2つの靴下は、彼は靴下の色、結局、二つの異なる色の靴下を身に着けていることは非常に恥ずかしいだろうについて非常に懸念しているかどうかのどちらかの側を気にしないでください。あなたのタスクはにあります彼は当然のことながら、できるだけ高く、この小さなZの確率を願って、彼は靴下の同じ確率、2つの色を取得することができますどのように古い、小さなZに語ったので、彼は彼らの選択を容易にするために、より多くの(L、R)を求めることができます。
アイデア:テンプレートのタイトル
間隔\([L、R] \ ) 答え:
\ [{\ sum_ {I色として現れる}和[I] \のCDOT( [i]の合計-1)/ 2} {C_ {Rオーバー\ -l + 1} ^ 2} \ ]
- お問い合わせのソート
- \([L、R] \)に\(L \)ブロックが第1キーである数は、rが小さい順に2番目のキーワードです。
- 暴力分子のメンテナンスは、答えの一部にすることができます
- 分子拡大がなった後の答えは、約2アウトながら、分子と分母で見つけることができます\([i]の和の\ CDOTの合計[I] -合計を[I] \) 全てのi、\のために見つけることができます([i]のSUM \)そして、それはなります\(R&LT。1-L + \)ので、我々は、すべて維持する必要があります\(合計[I] \)の正方形とすることができますが
#include <iostream>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;
const int N = 50010;
typedef long long ll;
int a[N],be[N],n,m;
ll res = 0,sum[N];
struct node{
int l,r,id;
ll A,B;
}q[N];
//如果在同一块,则按照右端点排序,否则按照左端点
bool cmp1(node a,node b){
return be[a.l] == be[b.l] ? a.r < b.r : a.l < b.l;
}
bool cmp(node a,node b){
return a.id < b.id;
}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a%b);}
ll S(ll x){return x * x;}
//先减去上一次的影响,修改后再重新加新的影响
void move(int pos,int add){res -= S(sum[a[pos]]);sum[a[pos]] += add;res += S(sum[a[pos]]);}
int main(){
scanf("%d%d",&n,&m);
int base = sqrt(n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);q[i].id = i;
be[i] = (i-1) / base + 1;//be[i]即i在第几块
}
for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r);
sort(q+1,q+1+m,cmp1);
int l = 1,r = 0;
res = 0;//res为当点询问区间内出现的所有颜色的个数平方和
for(int i=1;i<=m;i++){
//暴力调整区间,维护res
while(l < q[i].l)move(l++,-1);
while(l > q[i].l)move(--l,1);
while(r < q[i].r)move(++r,1);
while(r > q[i].r)move(r--,-1);
if(l == r){
q[i].A = 0;q[i].B = 1;continue;
}
q[i].A = res - (r - l + 1);//计算答案分子部分
q[i].B = 1ll * (r - l + 1) * (r - l);//分母部分
ll g = gcd(q[i].A,q[i].B);//约分
q[i].A /= g;
q[i].B /= g;
}
sort(q+1,q+1+m,cmp);
for(int i=1;i<=m;i++){
printf("%lld/%lld\n",q[i].A,q[i].B);
}
return 0;
}
通常のMoの最適化チーム
問い合わせが処理された最初のブロックで見つけることができた後に、位置rは、特に後方であるべきであるが、後に次のブロックに移動するとき、Rは、求めとして、ロットを前方に移動させてもよいです
//第一个块
1 50
2 100
//第二个块
12 13
14 100
【2100】問い合わせが完了した後、100〜> 13からのR、次いで13から - > 100。> 13 - これは明らかに> 100 100、100ほど良好ではありません。
どのように最適化するには?
隣接する二つの対向間のR照合します
さらに高速の順に昇順に、すなわち奇数ブロック、
結果 | メモリ | 時間 |
---|---|---|
容認されました | 3456キロバイト | 1840ミリ秒 |
容認されました | 3456キロバイト | 1392ミリ秒 |
以下は、最適化されています。
bool cmp1(node a,node b){
return be[a.l] == be[b.l] ?
(be[a.l]&1 ? a.r < b.r : a.r > b.r)
: a.l < b.l;
}
修正を加えたMoチーム
変更操作ができるかどうかの修正に参加する一般的な修理チームを考慮してください(O(1)\)\(だけでなく、回答の現在の範囲を維持するために)アプリケーションおよび失効を、それがすることができます(O(N ^ {5 \ 3オーバー\を})\)複雑内のすべての問い合わせに答えを見つけます。
実装:オフラインソート、順序トラバーサル問い合わせ、最初の時間の時間の現在の問い合わせに転送した後、通常のMO間隔と同じチームを転送した後。
方法ソート:ブロック長に設定されている(S_1、S_2を\)\(に従い、\(\ lfloor {S_1上のL \} \ rfloor \ {R&LT lfloor \ S_2オーバー} \ rfloor、T \) )トライアド大量注文、小規模\(のt \)は、いくつかの変更操作を尋ねる前に経験したこの瞬間を表し、
複雑性分析:各12枚の中に同じキーワードを求めて、それぞれの作品に呼び掛けシーケンスを考えます。この小さな、時間\(T \)まで変更\(m個\)を、各課題について\(L、R&LTの\)最大変化\(S_1、S_2 \) 、右総\(N ^ 2 \ {S_1、S_2}上\)そのようなブロック、隣接するブロック間の遷移の複雑されている\(O(N)\) 、合計複雑です
\(O(MS_1 + MS_2 + {N ^ 2メートル\ S_1S_2オーバー} + {N ^ 3 S_1S_2上\})\)
場合\(N、Mの\)と同じ順序で、取るとき\(S_1 = S_2 = N ^ {2 3上\} \) 場合に最適複雑であることができる\(N O(^ {5 \ 3オーバー})\ )
int l = 0, r = 0, t = 0, nowAns = 0;
inline void move(int pos, int sign) {
// update nowAns
}
inline void moveTime(int t, int sign) {
// apply or revoke modification
// update nowAns
}
void solve() {
BLOCK_SIZE = int(ceil(pow(n, 2.0 / 3)));
sort(querys, querys + m);
for (int i = 0; i < q1; ++i) {
const query q = querys[i];
while (t < q.t) moveTime(t++, 1);
while (t > q.t) moveTime(--t, -1);
while (l < q.l) move(l++, -1);
while (l > q.l) move(--l, 1);
while (r < q.r) move(r++, 1);
while (r > q.r) move(--r, -1);
ans[q.id] = nowAns;
}
}