互いに素なセット、分割統治、トポロジカルソート
互いに素セット
1:プラス側、2つのクエリ通信
復興ツリーを考えてみましょう、通信は今すぐ右のパス上の最大値となり、
LCTを維持するために動的エッジを追加する必要があるかもしれません
2:プラス側、最初の問い合わせ\(Iは\)時間\(X \)ブロックサイズユニコム
改造ツリーを検討していき
最初の右側の追求よりも本質的に大きい\(I \)の位置を
この時点でサイズが希望の答えです
3:最初のシングルポイントブラック、ホワイトのクエリ部分を維持
セット\(F_iと\)の\(私は\)の後ろの最初のゼロ
染色のシングルポイントの後
\(F_X = X + 1 \)
後\(Fの\)互いに素なセット配列として
質問
何色染色部ない4点
セグメントツリーは、直接再帰的に行うことができます
各点はそれだけで着色されるので、
だから、時間の複雑\(nlogn \)
互いに素セットと、私たちは同様のアプローチを検討
同上
各点は高々一度染めたので、
このため、時間複雑度が償却される\(O(N)\)
平方根セクションセクション5を加算
最初の非を見つけるために、それぞれの時間間隔\(1 \)位置を
暴力の平方根
平方根の数はあまりないので
互いに素セットメンテナンスと上記のように同じ
暴力のメンテナンス間隔の平方根とクエリのツリー状の配列した後、
6求\(\最大{a_iを* b_j * \ MIN_ {K = I} ^ jc_k} \)
それぞれに単調スタック処理\(C_I \)位置延びる単調
インターバル\(RMQ \)へ
互いに素セットでそれを行う方法を考えてみましょう
各スパンを考慮し、分裂を考えるとアプローチを征服\(中旬\)を拠出に答えます
プロセスの本質は逆さま互いに素セットパーティションです
我々は(私は\)\辺の重みは次のように定義されている\(MIN(C_I、C_ { I + 1})\)
右側の並べ替え
各互いに素セットメンテナンス\(、B \)最大
それぞれの統合がすることです\(私は、私は1 \ + ) どこリンクブロック合併
7⼀所与ツリー、各パスプラス側又は交差しない2つの点の間に少なくとも二つの側面があるかどうかを尋ねます
各時間は、操作は、本質的に、これらの二つの点を収縮の経路上の全ての点を境界付けています
互いに素セット圧縮ユニコムエンドポイントの後に、実質的に現在のブロックの最高点を維持するために、
ポイントの還元後の暴力ホップジャンプするたびに、時間の複雑さを保証することができます。
一見の時間複雑\(O(N)\)の
同じ間隔(scoi2016 Mengmengのピリダジン)
配列所与\(A \) 、(a_iを\ [0、K] \)\を与え\(m個\)制限、フォームの\は、([L1 ... R1] = [L2 ... R2] \ )
可能なシーケンスの最終的な数は、シーク
コレクションと直接的暴力と時間の複雑さを確認してください\(O(nmlogn)\)の
私たちは、の使用を検討し\(ST \)テーブル方式を
各部の動作は、に分割される\(\ログ)部分区間
セル間の対応関係
最後に、長さの分割間隔との関係\(1 \)間隔を得ることができます
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<map>
#define LL long long
using namespace std;
const int N = 1e5 + 3;
const LL mod = 1e9 + 7;
int fa[N * 21];
inline int getf(int x){
return fa[x] == x ? x : fa[x] = getf(fa[x]);
}
inline LL quick(LL a,LL b){
LL res = 1;
while(b){
if(b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline void add(int x,int y){
//printf("%d %d\n",x,y);
x = getf(x),y = getf(y);
fa[y] = x;
}
int n,m;
int main(){
n = read(),m = read();
for(int i = 1;i <= n * 20;++i) fa[i] = i;
for(int i = 1;i <= m;++i){
int l1 = read(),r1 = read(),l2 = read(),r2 = read();
int len = r1 - l1 + 1;
int now1 = l1,now2 = l2;
for(int i = 19;i >= 0;--i)
if(len & (1 << i)) {
add(i * n + now1,i * n + now2);
// printf("%d %d\n",i * n + now1,i * n + now2);
now1 += (1 << i);
now2 += (1 << i);
}
}
for(int i = 19;i >= 1;--i){
for(int j = 1;j <= n;++j){
int x = getf(i * n + j);
if(x != i * n + j){
add(i * n + j - n,x - n);
add(i * n + j - n + (1 << (i - 1)),x - n + (1 << (i - 1)));
}
}
}
LL sum = 0;
for(int i = 1;i <= n;++i) if(getf(i) == i) sum++;//cout << sum << endl;
printf("%lld\n",9 * quick(10ll,sum - 1) % mod);
return 0;
}
CF468B
我々はのためのことがわかりました
\(X \) 、ない場合\(A-X \)と\(-x B \)
だから、何も解決しません
そうでなければ、ノーであれば\(斧\)のみ\(のb \)内
何も存在しない場合- \(X \ b)は、その後だけで(\)\内には、
ある場合、我々は得ることができます\(X- \) 、\(A -x \) 、\(BXを\)一緒にしなければなりません
裁判官の前に合併プロセスのことを注意し、限りがないのと同じソリューションには解決策ではありません
同時に、この要素は必須の割り当てではありません終わりであれば、それはセットすることが可能です
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
const int N = 2e5 + 3;
map <int,int> m;
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
int n,a,b;
int fa[N];
int x[N];
inline int getf(int x){
return fa[x] == x ? x : fa[x] = getf(fa[x]);
}
int main(){
n = read(),a = read(),b = read();
for(int i = 1;i <= n;++i) x[i] = read(),m[x[i]] = i;
for(int i = 1;i <= n + 2;++i) fa[i] = i;
for(int i = 1;i <= n;++i){
if(m[a - x[i]] == 0 && m[b - x[i]] == 0){
printf("NO\n");
return 0;
}
if(m[a - x[i]] == 0){
int g = getf(i);
int gg = getf(m[b - x[i]]);
if(g == getf(n + 1) || gg == getf(n + 1)){
printf("NO\n");
return 0;
}
fa[gg] = fa[g] = getf(n + 2);
}
else if(m[b - x[i]] == 0){
int g = getf(i);
int gg = getf(m[a - x[i]]);
if(g == getf(n + 2) || gg == getf(n + 2)){
printf("NO\n");
return 0;
}
fa[gg] = fa[g] = getf(n + 1);
}
else {
int g = getf(i);int now = 0;
int xx = getf(m[a - x[i]]);
int xxx = getf(m[b - x[i]]);
int n1 = getf(n + 1),n2 = getf(n + 2);
if(g == n1 || xx == n1 || xxx == n1) now++;
if(xxx == n2|| g == n2 || xx == n2) now++;
if(now == 2){
printf("NO\n");
return 0;
}
fa[g] = xxx;
fa[xx] = xxx;
}
}
/// cout << "GG" << endl;
// for(int i = 1;i <= n;++i){
// int x = getf(i);
// if(x != getf(n + 1) && x != getf(n + 2)){
// printf("NO\n");
// return 0;
// }
// }
printf("YES\n");
for(int i = 1;i <= n;++i){
int x = getf(i);
if(x == getf(n + 1)) printf("0 ");
else printf("1 ");
}
return 0;
}
BZOJ2054
私たちは、質問4の互いに素セットの上に、我々はすべての操作が行わ逆だろう、問題になることがわかりました
うまく動作します直接のアルゴリズム
しかし、注意してください
前処理した場合には\(FA \)の配列は、疑いいくつかの多くに対処する必要があります
この問題が処理されていない場合\(fa_は{N + 1} \) 表示\(fa_ {N + 1} = 0 \) 各ジャンプにつながる条件\は、(N + 1 \)時間をスキップする\ (0 \)死のサイクル
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<map>
#define LL long long
using namespace std;
const int N = 1e6 + 3;
int fa[N];
int c[N];
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline int getf(int x){
return x == fa[x] ? x : fa[x] = getf(fa[x]);
}
int n,m,p,q;
int main(){
n = read(),m = read(),p = read(),q = read();
for(int i = 1;i <= n + 1;++i) fa[i] = i;
for(int i = m;i >= 1;--i){
// cout << i << endl;
int l = (1ll * i * p + q) % n + 1;
int r = (1ll * i * q + p) % n + 1;
if(l > r) swap(l,r);
// printf("l:%d r:%d\n",l,r);
int x = getf(l);
while(x <= r){
c[x] = i;
int t = getf(x + 1);
fa[x] = t;
x = t;
}
}
for(int i = 1;i <= n;++i) printf("%d\n",c[i]);
return 0;
}
NOIプログラム自動的に解析し、(必須オンライン版)
各互いに素セットベクトルを維持するためには、彼らは現在のコレクションに関係同値いないと述べました
ベクトルヒューリスティックマージのたびに統合
合併、あまり暴力的な列挙ベクトルに先立ち、マージするかどうかを判断します
それは別のコレクションで不平等な関係かどうかを確認するために、あります
時間複雑\(MLOG(N)\)
BZOJ3712
クラスカルツリーの再構築
私たちは、各操作のための統合ノードは、新たに追加されたノードを持っているので、我々はそれぞれの合併について、クラスカルのツリーで、LCAの彼らの再建に行われなければなりません
言い換えれば、私たちは、新しいツリーノードクラスカル再建のこの時点で合併関係のポイントで維持する必要が
それぞれの残りなどを使用してマージ
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<vector>
#include<algorithm>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 5e5 + 3;
LL ans;
int n,m,k,tot,root;
int g[N],head[N];
int deep[N];
int st[21][N];
int fa[N];
struct opt{
int from;
int to;
}a[N];
vector <pii> G[N];
struct edge{
int to;
int nxt;
}e[N << 1];
vector <pii> s;
inline void add(int x,int y){
e[++tot].to = y;
e[tot].nxt = head[x];
head[x] = tot;
}
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline int getf(int x){
return fa[x] == x ? x : fa[x] = getf(fa[x]);
}
inline int dfs(int x,int f,int dep){
st[0][x] = f;
deep[x] = dep;
for(int i = head[x];i;i = e[i].nxt){
int y = e[i].to;
dfs(y,x,dep + 1);
}
}
inline int lca(int x,int y){
if(deep[x] < deep[y]) swap(x,y);
for(int i = 20;i >= 0;--i) if(deep[st[i][x]] >= deep[y]) x = st[i][x];
if(x == y) return x;
for(int i = 20;i >= 0;--i)
if(st[i][x] != st[i][y]) x = st[i][x],y = st[i][y];
return st[0][x];
}
int main(){
n = read(),m = read(),k = read();
for(int i = 1;i <= n;++i) g[i] = read();
for(int i = 1;i <= n * 2;++i) fa[i] = i;
for(int i = 1;i <= m;++i){
a[i].from = read();
a[i].to = read();
int x = getf(a[i].from),y = getf(n + i);
add(y,x);fa[x] = y;
x = getf(a[i].to);
add(y,x);fa[x] = y;
}
for(int i = 1;i <= n + m;++i){
if(!deep[i])
dfs(getf(i),0,1);
}
for(int i = 1;i <= 20;++i){
for(int j = 1;j <= n + m;++j)
st[i][j] = st[i - 1][st[i - 1][j]];
}
for(int i = 1;i <= k;++i){
int x = read(),y = read();
int L = lca(x,y);
G[L].push_back(mk(x,y));
}
for(int i = n + 1;i <= n + m;++i){
// printf("i:%d\n",i);
for(int j = 0;j < (int)G[i].size();++j){
// printf("%d %d\n",G[i][j].fi,G[i][j].se);
LL x = min(g[G[i][j].fi],g[G[i][j].se]);
ans += 1ll * x * 2;
g[G[i][j].fi] -= x;
g[G[i][j].se] -= x;
}
}
cout << ans << endl;
return 0;
}
NOI2018リターンジャーニー
オフラインは確かにうまくやります
私たちは、硬さでソートし、検索します
側面は参照次いで、リストに追加条件を満たし続け\(1 \)の数、フォーカスチェックポイント\(dis_x \)最小
これは、我々がオフラインで考えることができるされているアプローチであります
私たちのオンラインのアプローチについては、クラスカルツリーの再構築を検討し、その本質は互いに素セットです
分治
多項式乗算
多項式\(F(X)* G (X)\) 値
もちろん、これは直接することができ、\(FFT \)
私たちは理解して、パーティションのアイデアを考えます
设\(F(x)は(X)* X ^ {\ FRAC {n}は{2}} = + B(X)\)
\(G(x)= C(X)* X ^ {\ FRAC {n}は{2}} + D(X)\)
\(F(X)* G(X)= A(x)をC(x)は、x ^のN +(B(x)はC(X)+ A(x)はD(x))を、X ^ {\ FRAC {N } {2}} + B(x)はD(X)\)
ZJOI2016
それぞれが交差考えてみましょう\(中旬\)答えを、私たちは、