zoto
Code1
樹狀數組套動態開點權值線段樹
效仿HH的項鏈,維護右端點,詢問需要排序
#include<bits/stdc++.h>
using namespace std
;
template <class T=int> T
rd()
{T res
=0;char ch
=getchar();while(!isdigit(ch
)) ch
=getchar();while( isdigit(ch
)) res
=(res
<<1)+(res
<<3)+(ch
^48),ch
=getchar();return res
;
}
const int N
=100010;
int a
[N
],n
,m
,ans
[N
],last
[N
];
struct nodeq
{int l
,r
,L
,R
,id
;bool operator<(const nodeq
& o
)const{return r
<o
.r
;}
}q
[N
];
struct node
{int l
,r
,v
;
}tree
[N
*200];
int rt
[N
],cnt
;
void update(int &u
,int l
,int r
,int pos
,int v
)
{if(!u
) tree
[u
=++cnt
]={0,0,0};tree
[u
].v
+=v
;if(l
==r
) return;int mid
=l
+r
>>1;if(pos
<=mid
) update(tree
[u
].l
,l
,mid
,pos
,v
);if(pos
>mid
) update(tree
[u
].r
,mid
+1,r
,pos
,v
);
}
int query(int u
,int l
,int r
,int L
,int R
)
{if(!u
) return 0;if(L
<=l
&&r
<=R
) return tree
[u
].v
;int mid
=l
+r
>>1;int v
=0;if(L
<=mid
) v
+=query(tree
[u
].l
,l
,mid
,L
,R
);if(R
>mid
) v
+=query(tree
[u
].r
,mid
+1,r
,L
,R
);return v
;
}
int lowbit(int x
){return x
&-x
;}
void add(int k
,int pos
,int v
)
{for(;k
<=n
;k
+=lowbit(k
)) update(rt
[k
],0,100000,pos
,v
);
}
int ask(int k
,int L
,int R
)
{int ans
=0;for(;k
;k
-=lowbit(k
)) ans
+=query(rt
[k
],0,100000,L
,R
);return ans
;
}void init()
{for(int i
=1;i
<=100000;i
++) last
[i
]=0;for(int i
=1;i
<=n
;i
++) rt
[i
]=0;cnt
=0;for(int i
=0;i
<=cnt
;i
++) tree
[i
].l
=tree
[i
].r
=tree
[i
].v
=0;
}
int main()
{int Tc
=rd();while(Tc
--){init();n
=rd(),m
=rd();for(int i
=1;i
<=n
;i
++) a
[i
]=rd();for(int i
=1;i
<=m
;i
++){int x0
=rd(),y0
=rd(),x1
=rd(),y1
=rd();q
[i
]={x0
,x1
,y0
,y1
,i
};}sort(q
+1,q
+1+m
);int k
=1;for(int i
=1;i
<=m
;i
++){for(;k
<=q
[i
].r
;k
++){int v
=a
[k
];if(last
[v
]) add(last
[v
],v
,-1);add(k
,v
,1);last
[v
]=k
;}ans
[q
[i
].id
]=ask(q
[i
].r
,q
[i
].L
,q
[i
].R
)-ask(q
[i
].l
-1,q
[i
].L
,q
[i
].R
);}for(int i
=1;i
<=m
;i
++) printf("%d\n",ans
[i
]);}
}
Code2
樹狀數組套動態開點權值線段樹
校dl的寫法,不需要對詢問進行排序
#include<bits/stdc++.h>
using namespace std
;
template <class T=int> T
rd()
{T res
=0;char ch
=getchar();while(!isdigit(ch
)) ch
=getchar();while( isdigit(ch
)) res
=(res
<<1)+(res
<<3)+(ch
^48),ch
=getchar();return res
;
}
const int N
=100010;
int a
[N
],n
,m
,ans
[N
],last
[N
];
struct nodeq
{int l
,L
,R
,id
;
};
vector
<nodeq
> q
[N
];
struct node
{int l
,r
,v
;
}tree
[N
*200];
int rt
[N
],cnt
,lim
;
void update(int &u
,int l
,int r
,int pos
,int v
)
{if(!u
) u
=++cnt
;tree
[u
].v
+=v
;if(l
==r
) return;int mid
=l
+r
>>1;if(pos
<=mid
) update(tree
[u
].l
,l
,mid
,pos
,v
);if(pos
>mid
) update(tree
[u
].r
,mid
+1,r
,pos
,v
);
}
int query(int u
,int l
,int r
,int L
,int R
)
{if(!u
) return 0;if(L
<=l
&&r
<=R
) return tree
[u
].v
;int mid
=l
+r
>>1;int v
=0;if(L
<=mid
) v
+=query(tree
[u
].l
,l
,mid
,L
,R
);if(R
>mid
) v
+=query(tree
[u
].r
,mid
+1,r
,L
,R
);return v
;
}
int lowbit(int x
){return x
&-x
;}
void add(int k
,int pos
,int v
)
{for(;k
<=n
;k
+=lowbit(k
)) update(rt
[k
],0,lim
,pos
,v
);
}
int ask(int k
,int L
,int R
)
{int ans
=0;for(;k
;k
-=lowbit(k
)) ans
+=query(rt
[k
],0,lim
,L
,R
);return ans
;
}
void init()
{for(int i
=0;i
<=lim
;i
++) last
[i
]=0;for(int i
=0;i
<=n
;i
++) rt
[i
]=0;for(int i
=0;i
<=cnt
;i
++) tree
[i
].l
=tree
[i
].r
=tree
[i
].v
=0;cnt
=0;for(int i
=0;i
<=n
;i
++) q
[i
].clear();
}
int main()
{int Tc
=rd();while(Tc
--){init();n
=rd(),m
=rd();for(int i
=1;i
<=n
;i
++) a
[i
]=rd();lim
=*max_element(a
+1,a
+1+n
);for(int i
=1;i
<=m
;i
++){int x0
=rd(),y0
=rd(),x1
=rd(),y1
=rd();q
[x1
].push_back({x0
,y0
,y1
,i
});}for(int i
=1;i
<=n
;i
++){if(last
[a
[i
]]) add(last
[a
[i
]],a
[i
],-1);add(i
,a
[i
],1);last
[a
[i
]]=i
;for(auto t
:q
[i
])ans
[t
.id
]=ask(i
,t
.L
,t
.R
)-ask(t
.l
-1,t
.L
,t
.R
);}for(int i
=1;i
<=m
;i
++) printf("%d\n",ans
[i
]);}
}
Code3
std寫法
首先來看一個子問題:給一個數組,初始所有位置全是0,每次單點加一或減一,詢問區間有多少個位置不為0。也許你很快會給出一個修改O(logn)查詢O(logn)的優秀做法。那么如果強制規定修改O(1),查詢能做到什么效率?我們去考慮分塊,維護兩個數組num[i]和sum[i]分別維護第i位置上的值,以及第i塊內的值不為0的位置個數。那么每次單點修改只需要修改num[i]和sum[i/block_size]兩個位置的值,然后查詢的時候需要O(sqrtn)效率的去查詢區間所覆蓋的完整塊的值以及區間兩端散塊的值(散塊值可以沒有)。這顯然是一個修改O(1),查詢O(sqrtn)的做法。
然后考慮這個題,我們發現可以用莫隊去維護詢問的區間,即x坐標。y(fx)維度單獨拎出來就變成了上述子問題。我們會神奇的發現,這樣的復雜度是:O(nsqrt(n)1+m1sqrt(n))的,莫隊單次修改時O(sqrtn)的復雜度遇上了值域上O(1)的修改,莫隊單次查詢時O(1)的復雜度遇上了值域查詢時O(sqrt(n))的復雜度。
#include<bits/stdc++.h>using namespace std
;
using ll
=long long;
template <class T=int> T
rd()
{T res
=0;char ch
=getchar();while(!isdigit(ch
)) ch
=getchar();while( isdigit(ch
)) res
=(res
<<1)+(res
<<3)+(ch
^48),ch
=getchar();return res
;
}
const int N
=100010;
int a
[N
],n
,m
;int b
[N
],sz
;
struct nodeq
{int l
,r
,L
,R
,id
;bool operator<(const nodeq
&o
)const{if(b
[l
]==b
[o
.l
]){if(b
[l
]&1)return r
<o
.r
;elsereturn r
>o
.r
;}return b
[l
]<b
[o
.l
];}
}q
[N
];
int num
[N
],sum
[N
],ans
[N
];
void add(int k
){if(++num
[k
]==1) sum
[b
[k
]]++;}
void sub(int k
){if(--num
[k
]==0) sum
[b
[k
]]--;}
int query(int k
)
{int res
=0;for(int i
=1;i
<b
[k
];i
++) res
+=sum
[i
];for(int i
=(b
[k
]-1)*sz
+1;i
<=k
;i
++) res
+=(num
[i
]>=1);return res
;
}
int main()
{int Tc
=rd();while(Tc
--){n
=rd(),m
=rd(),sz
=313;for(int i
=1;i
<=n
;i
++) a
[i
]=rd();for(int i
=1;i
<=n
;i
++) sum
[i
]=num
[i
]=0;for(int i
=1;i
<=n
;i
++) b
[i
]=(i
-1)/sz
+1;for(int i
=1;i
<=m
;i
++){int x0
=rd(),y0
=rd(),x1
=rd(),y1
=rd();q
[i
]={x0
,x1
,y0
,y1
,i
};}sort(q
+1,q
+1+m
);int l
=1,r
=0;for(int i
=1;i
<=m
;i
++){while(l
<q
[i
].l
) sub(a
[l
++]);while(l
>q
[i
].l
) add(a
[--l
]);while(r
<q
[i
].r
) add(a
[++r
]);while(r
>q
[i
].r
) sub(a
[r
--]);ans
[q
[i
].id
]=query(q
[i
].R
)-query(q
[i
].L
-1);}for(int i
=1;i
<=m
;i
++) printf("%d\n",ans
[i
]);}
}
Code4
把種類查詢轉換偏序查詢的常規操作:指向上一個出現的位置當且僅當上一次出現的位置在區間外面時計算貢獻
cdq分治+bit
對于每個詢問的右端點也看作一個時間軸,如果該點可能對詢問產生貢獻,那么下一個這個點的橫坐標嚴格大于詢問右端點。
a1≤a1b1≤b2c1>c2a_1\leq a_1\\ b_1\leq b_2 \\c_1>c_2a1?≤a1?b1?≤b2?c1?>c2?
cdq分治轉化為平面二維數點,然后差分+bit即可統計。
對于每個詢問的左端點也看作一個時間軸,如果該點可能對詢問產生貢獻,那么前一個這個點的橫坐標嚴格小于詢問左端點。
本質都種類→\to→偏序
#include<bits/stdc++.h>
using namespace std
;
template <class T=int> T
rd()
{T res
=0;char ch
=getchar();while(!isdigit(ch
)) ch
=getchar();while( isdigit(ch
)) res
=(res
<<1)+(res
<<3)+(ch
^48),ch
=getchar();return res
;
}
const int N
=100010;
int a
[N
],n
,m
,ans
[N
],last
[N
];
int ne
[N
],pos
[N
];
struct nodeq
{int op
;int a
,b
,c
;int fg
,id
;
}q
[N
<<4];
int fw
[N
];
int lowbit(int x
){return x
&-x
;}
void update(int k
,int v
){for(;k
<=n
+1;k
+=lowbit(k
)) fw
[k
]+=v
;}
int qsum(int k
){int v
=0;for(;k
;k
-=lowbit(k
)) v
+=fw
[k
];return v
;}void solve(int l
,int r
)
{if(l
>=r
) return;int mid
=l
+r
>>1;solve(l
,mid
),solve(mid
+1,r
);int i
=l
;for(int j
=mid
+1;j
<=r
;j
++){while(i
<=mid
&&q
[i
].b
<=q
[j
].b
) {if(q
[i
].op
==0) update(q
[i
].c
,1);i
++;}if(q
[j
].op
==1) ans
[q
[j
].id
]+=q
[j
].fg
*(qsum(n
+1)-qsum(q
[j
].c
));}while(i
>l
) {--i
;if(q
[i
].op
==0) update(q
[i
].c
,-1);}inplace_merge(q
+l
,q
+mid
+1,q
+r
+1,[](const nodeq
&x
,const nodeq
&y
){return x
.b
<y
.b
||x
.b
==y
.b
&&x
.a
<y
.a
;});
}
int main()
{int Tc
=rd();while(Tc
--){n
=rd(),m
=rd();for(int i
=1;i
<=n
;i
++) a
[i
]=rd();for(int i
=1;i
<=m
;i
++) ans
[i
]=0;for(int i
=0;i
<=100000;i
++) pos
[a
[i
]]=n
+1;for(int i
=n
;i
>=1;i
--) {ne
[i
]=pos
[a
[i
]];pos
[a
[i
]]=i
;}int cnt
=0;for(int i
=1;i
<=n
;i
++) q
[++cnt
]={0,i
,a
[i
],ne
[i
]};for(int i
=1;i
<=m
;i
++){int x0
=rd(),y0
=rd(),x1
=rd(),y1
=rd();q
[++cnt
]={1,x0
-1,y0
-1,x1
,1,i
};q
[++cnt
]={1,x0
-1,y1
,x1
,-1,i
};q
[++cnt
]={1,x1
,y0
-1,x1
,-1,i
};q
[++cnt
]={1,x1
,y1
,x1
,1,i
};}sort(q
+1,q
+1+cnt
,[](const nodeq
&x
,const nodeq
&y
){return x
.a
<y
.a
||x
.a
==y
.a
&&x
.b
<y
.b
;});solve(1,cnt
);for(int i
=1;i
<=m
;i
++) printf("%d\n",ans
[i
]);}
}
Code5
和Code4基本一樣
#include<bits/stdc++.h>
using namespace std
;
template <class T=int> T
rd()
{T res
=0;char ch
=getchar();while(!isdigit(ch
)) ch
=getchar();while( isdigit(ch
)) res
=(res
<<1)+(res
<<3)+(ch
^48),ch
=getchar();return res
;
}
const int N
=100010;
int a
[N
],n
,m
,ans
[N
];
int last
[N
],pos
[N
];
struct nodeq
{int op
;int a
,b
,c
;int fg
,id
;
}q
[N
<<4];
int fw
[N
];
int lowbit(int x
){return x
&-x
;}
void update(int k
,int v
){if(!k
) return fw
[k
]+=v
,void();for(;k
<=n
;k
+=lowbit(k
)) fw
[k
]+=v
;}
int qsum(int k
){int v
=fw
[0];for(;k
;k
-=lowbit(k
)) v
+=fw
[k
];return v
;}void solve(int l
,int r
)
{if(l
>=r
) return;int mid
=l
+r
>>1;solve(l
,mid
),solve(mid
+1,r
);int i
=l
;for(int j
=mid
+1;j
<=r
;j
++){while(i
<=mid
&&q
[i
].b
<=q
[j
].b
) {if(q
[i
].op
==0) update(q
[i
].c
,1);i
++;}if(q
[j
].op
==1) ans
[q
[j
].id
]+=q
[j
].fg
*qsum(q
[j
].c
);}while(i
>l
) {--i
;if(q
[i
].op
==0) update(q
[i
].c
,-1);}inplace_merge(q
+l
,q
+mid
+1,q
+r
+1,[](const nodeq
&x
,const nodeq
&y
){return x
.b
<y
.b
||x
.b
==y
.b
&&x
.a
<y
.a
;});
}
int main()
{int Tc
=rd();while(Tc
--){n
=rd(),m
=rd();for(int i
=1;i
<=n
;i
++) a
[i
]=rd();for(int i
=1;i
<=m
;i
++) ans
[i
]=0;for(int i
=0;i
<=100000;i
++) pos
[a
[i
]]=0;for(int i
=1;i
<=n
;i
++) {last
[i
]=pos
[a
[i
]];pos
[a
[i
]]=i
;}int cnt
=0;for(int i
=1;i
<=n
;i
++) q
[++cnt
]={0,i
,a
[i
],last
[i
]};for(int i
=1;i
<=m
;i
++){int x0
=rd(),y0
=rd(),x1
=rd(),y1
=rd();q
[++cnt
]={1,x0
-1,y0
-1,x0
-1,1,i
};q
[++cnt
]={1,x0
-1,y1
,x0
-1,-1,i
};q
[++cnt
]={1,x1
,y0
-1,x0
-1,-1,i
};q
[++cnt
]={1,x1
,y1
,x0
-1,1,i
};}sort(q
+1,q
+1+cnt
,[](const nodeq
&x
,const nodeq
&y
){return x
.a
<y
.a
||x
.a
==y
.a
&&x
.b
<y
.b
;});solve(1,cnt
);for(int i
=1;i
<=m
;i
++) printf("%d\n",ans
[i
]);}
}
總結
以上是生活随笔為你收集整理的2021“MINIEYE杯”中国大学生算法设计超级联赛(1)zoto(二维数颜色)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。