[UOJ 128][NOI 2015]パッケージマネージャ(ツリー分割+セグメントツリー)
#128.【NOI 2015】パッケージマネージャ
LinuxユーザーとOS Xユーザーは、パッケージマネージャに慣れていないに違いありません.パッケージマネージャを使用すると、1行のコマンドでパッケージをインストールできます.パッケージマネージャは、ソフトウェアソースからパッケージをダウンロードするのに役立ちます.同時に、すべての依存(つまり、このパッケージをインストールするインストールに依存する他のパッケージをダウンロードする)を自動的に解決し、すべての構成を完了します.Debian/Ubuntuで使用されているapt-get、Fedora/centOSで使用されているyum、およびOS Xで使用可能なhomebrewは、優れたパッケージマネージャです.
あなたは自分のパッケージマネージャを設計することにしました.ソフトウェアパッケージ間の依存問題を解決することは避けられません.パッケージA A AがパッケージBに依存している場合、パッケージA Aをインストールする前に、パッケージBをインストールする必要があります.また、パッケージB Bをアンインストールする場合は、パッケージA Aをアンインストールする必要があります.すべてのパッケージ間の依存関係を取得しました.また、あなたの前の仕事のため、0 0番のパッケージを除いて、あなたのマネージャの中のパッケージは1つのパッケージに依存し、1つのパッケージに依存しますが、0番のパッケージはいずれのパッケージにも依存しません.依存関係にループは存在しない(m m(m≧2)(m≧2)個のパッケージA 1,A 2,A 3,...,AmA 1,A 2,A 3,...,A 1 A 1がA 2 A 2 A 2,A 2 A 2がA 3 A 3,A 3 A 3がA 4 A 4,...,Am−1 A m−1がAm A 1 mに依存し,Am A mがA 1 A 1に依存するということは,このm個のパッケージの依存関係がループを構成しているという)はもちろん,一つのパッケージも自分に依存しない.
今、パッケージマネージャに依存ソリューションを書きます.フィードバックによると、ユーザーは、パッケージをインストールおよびアンインストールするときに、この操作が実際にどのくらいのパッケージのインストール状態を変更するか(すなわち、インストール操作がインストールされていないパッケージを何個インストールするか、またはインストール操作がインストールされているパッケージを何個アンインストールするか)を迅速に知りたいと考えています.あなたのタスクは、この部分を実現することです.インストール済みのパッケージをインストールしたり、インストールされていないパッケージをアンインストールしたりしても、パッケージのインストール状態は変わりません.つまり、この場合、インストール状態を変更するパッケージの数は0です.
入力フォーマット
入力ファイルの1行目には、パッケージの合計数を示す1つの正の整数nが含まれている.パッケージは0から番号付けされます.
その後、1行はn−1 n−1個の整数を含み、隣接する整数間は、1,2,3,…,n−2,n−1,2,3,…,n−2,n−1,n−1,n−1,n−1,n−1番のパッケージ依存パッケージの番号をそれぞれ表す単一のスペースで区切られる.
次の行は、1つの正の整数q qを含み、クエリの総数を表す.
その後q行、行ごとに1つの問い合わせがあります.質問は2つに分けられます. install x:インストールパッケージx を示す uninstall x:アンインストールパッケージx を示す
各パッケージのインストールステータスを維持する必要があります.最初はすべてのパッケージがインストールされていません.各操作について、この操作を出力すると、どのくらいのパッケージのインストールステータスが変更され、その後、この操作を適用する必要があります(つまり、メンテナンスのインストールステータスを変更します).
出力フォーマット
出力ファイルにはq q行が含まれます.
出力ファイルのi行目は1つの整数を出力し、iステップ目の操作でインストール状態を変更したパッケージ数です.
サンプル1
input
output
explanation
最初はすべてのパッケージがインストールされていませんでした.
5番のパッケージをインストールするには、0,1,5 0,1,5の3つのパッケージをインストールする必要があります.
その後、6番のパッケージをインストールし、6番のパッケージをインストールするだけです.このとき,0,1,5,6 0,1,5,6の4つのパッケージがインストールされた.
1番のパッケージをアンインストールするには、1,5,6 1,5,6の3つのパッケージをアンインストールする必要があります.この時点で、0番のパッケージだけがインストールされています.
その後、4番のパッケージをインストールするには、1,4 1,4の2つのパッケージをインストールする必要があります.このとき0,1,4 0,1,4は実装状態にある.
最後に、0 0番のパッケージをアンインストールすると、すべてのパッケージがアンインストールされます.
サンプル2
input
output
サンプル3
サンプルデータのダウンロードを参照してください.
制限と約束
テストポイント番号
n nの規模
q qの規模
コメント
1
n=5000 n = 5000
q=5000 q = 5000
2
3
n=100000 n = 100000
q=100000 q = 100000
データにアンインストール操作は含まれていません
4
5
n=100000 n = 100000
q=100000 q = 100000
iと番号付けされたパッケージに依存するパッケージ番号は、[0,i−1][0,i−1]内で均一にランダムである.各動作のパッケージ番号は[0,n−1][0,n−1]内で均一にランダムである.
6
7
8
9
n=100000 n = 100000
q=100000 q = 100000
10
11
12
13
14
15
16
17
18
19
20
時間制限:1 s 1 s
スペース制限:512 MB 512 MB
考え方&分析
この問題は、1つのソフトウェアがダウンロードされていないことを0で表し、1はすでにダウンロードされていることを示しています.この問題には2つの操作があり、uninstall xを操作することはxをルートとするサブツリーのすべてのinstall済みのソフトウェアをアンインストールすることであり、xと他のサブツリーの値をすべて0に割り当てることである.一方,install xを操作すると,ルートからxへのパス上のすべてのパッケージ(xを含む)をすべてダウンロードし,1つのパス上のすべてのポイントに1を付与すると見なすことができる.簡単に分析すると、これはツリーの裸の問題であり、セグメントツリーですべての値を維持すればよいことがわかります.答えについては,修正するたびにルートのsumを記録し,sumと操作後のルートのsumの絶対値が答えである.
Code
LinuxユーザーとOS Xユーザーは、パッケージマネージャに慣れていないに違いありません.パッケージマネージャを使用すると、1行のコマンドでパッケージをインストールできます.パッケージマネージャは、ソフトウェアソースからパッケージをダウンロードするのに役立ちます.同時に、すべての依存(つまり、このパッケージをインストールするインストールに依存する他のパッケージをダウンロードする)を自動的に解決し、すべての構成を完了します.Debian/Ubuntuで使用されているapt-get、Fedora/centOSで使用されているyum、およびOS Xで使用可能なhomebrewは、優れたパッケージマネージャです.
あなたは自分のパッケージマネージャを設計することにしました.ソフトウェアパッケージ間の依存問題を解決することは避けられません.パッケージA A AがパッケージBに依存している場合、パッケージA Aをインストールする前に、パッケージBをインストールする必要があります.また、パッケージB Bをアンインストールする場合は、パッケージA Aをアンインストールする必要があります.すべてのパッケージ間の依存関係を取得しました.また、あなたの前の仕事のため、0 0番のパッケージを除いて、あなたのマネージャの中のパッケージは1つのパッケージに依存し、1つのパッケージに依存しますが、0番のパッケージはいずれのパッケージにも依存しません.依存関係にループは存在しない(m m(m≧2)(m≧2)個のパッケージA 1,A 2,A 3,...,AmA 1,A 2,A 3,...,A 1 A 1がA 2 A 2 A 2,A 2 A 2がA 3 A 3,A 3 A 3がA 4 A 4,...,Am−1 A m−1がAm A 1 mに依存し,Am A mがA 1 A 1に依存するということは,このm個のパッケージの依存関係がループを構成しているという)はもちろん,一つのパッケージも自分に依存しない.
今、パッケージマネージャに依存ソリューションを書きます.フィードバックによると、ユーザーは、パッケージをインストールおよびアンインストールするときに、この操作が実際にどのくらいのパッケージのインストール状態を変更するか(すなわち、インストール操作がインストールされていないパッケージを何個インストールするか、またはインストール操作がインストールされているパッケージを何個アンインストールするか)を迅速に知りたいと考えています.あなたのタスクは、この部分を実現することです.インストール済みのパッケージをインストールしたり、インストールされていないパッケージをアンインストールしたりしても、パッケージのインストール状態は変わりません.つまり、この場合、インストール状態を変更するパッケージの数は0です.
入力フォーマット
入力ファイルの1行目には、パッケージの合計数を示す1つの正の整数nが含まれている.パッケージは0から番号付けされます.
その後、1行はn−1 n−1個の整数を含み、隣接する整数間は、1,2,3,…,n−2,n−1,2,3,…,n−2,n−1,n−1,n−1,n−1,n−1番のパッケージ依存パッケージの番号をそれぞれ表す単一のスペースで区切られる.
次の行は、1つの正の整数q qを含み、クエリの総数を表す.
その後q行、行ごとに1つの問い合わせがあります.質問は2つに分けられます.
各パッケージのインストールステータスを維持する必要があります.最初はすべてのパッケージがインストールされていません.各操作について、この操作を出力すると、どのくらいのパッケージのインストールステータスが変更され、その後、この操作を適用する必要があります(つまり、メンテナンスのインストールステータスを変更します).
出力フォーマット
出力ファイルにはq q行が含まれます.
出力ファイルのi行目は1つの整数を出力し、iステップ目の操作でインストール状態を変更したパッケージ数です.
サンプル1
input
7
0 0 0 1 1 5
5
install 5
install 6
uninstall 1
install 4
uninstall 0
output
3
1
3
2
3
explanation
最初はすべてのパッケージがインストールされていませんでした.
5番のパッケージをインストールするには、0,1,5 0,1,5の3つのパッケージをインストールする必要があります.
その後、6番のパッケージをインストールし、6番のパッケージをインストールするだけです.このとき,0,1,5,6 0,1,5,6の4つのパッケージがインストールされた.
1番のパッケージをアンインストールするには、1,5,6 1,5,6の3つのパッケージをアンインストールする必要があります.この時点で、0番のパッケージだけがインストールされています.
その後、4番のパッケージをインストールするには、1,4 1,4の2つのパッケージをインストールする必要があります.このとき0,1,4 0,1,4は実装状態にある.
最後に、0 0番のパッケージをアンインストールすると、すべてのパッケージがアンインストールされます.
サンプル2
input
10
0 1 2 1 3 0 0 3 2
10
install 0
install 3
uninstall 2
install 7
install 5
install 9
uninstall 9
install 4
install 1
install 9
output
1
3
2
1
3
1
1
1
0
1
サンプル3
サンプルデータのダウンロードを参照してください.
制限と約束
テストポイント番号
n nの規模
q qの規模
コメント
1
n=5000 n = 5000
q=5000 q = 5000
2
3
n=100000 n = 100000
q=100000 q = 100000
データにアンインストール操作は含まれていません
4
5
n=100000 n = 100000
q=100000 q = 100000
iと番号付けされたパッケージに依存するパッケージ番号は、[0,i−1][0,i−1]内で均一にランダムである.各動作のパッケージ番号は[0,n−1][0,n−1]内で均一にランダムである.
6
7
8
9
n=100000 n = 100000
q=100000 q = 100000
10
11
12
13
14
15
16
17
18
19
20
時間制限:1 s 1 s
スペース制限:512 MB 512 MB
考え方&分析
この問題は、1つのソフトウェアがダウンロードされていないことを0で表し、1はすでにダウンロードされていることを示しています.この問題には2つの操作があり、uninstall xを操作することはxをルートとするサブツリーのすべてのinstall済みのソフトウェアをアンインストールすることであり、xと他のサブツリーの値をすべて0に割り当てることである.一方,install xを操作すると,ルートからxへのパス上のすべてのパッケージ(xを含む)をすべてダウンロードし,1つのパス上のすべてのポイントに1を付与すると見なすことができる.簡単に分析すると、これはツリーの裸の問題であり、セグメントツリーですべての値を維持すればよいことがわかります.答えについては,修正するたびにルートのsumを記録し,sumと操作後のルートのsumの絶対値が答えである.
Code
#include
using namespace std;
typedef long long ll;
template<class T>inline void read(T &x) {
x=0;T f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
x*=f;
}
#define rd(x) read(x)
#define r2(x,y) rd(x),rd(y)
#define r3(x,y,z) r2(x,y),rd(z)
#define r4(x,y,z,o) r2(x,y),r2(z,o)
#define ls o<<1
#define rs o<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int maxn=1000010;
int n,q,fa[maxn],dep[maxn],tp[maxn],son[maxn],id[maxn],dfs_clock,sz[maxn];
vector<int>G[maxn];
struct Seg {
int sum,upd;
inline void init() {
sum=0;
upd=-1;
}
}t[maxn<<2];
inline void pushup(int o) {
t[o].sum=t[ls].sum+t[rs].sum;
}
inline void pushdown(int o,int l,int r) {
if(t[o].upd!=-1) {
int mid=(l+r)>>1;
t[ls].sum=t[o].upd*(mid-l+1);
t[rs].sum=t[o].upd*(r-mid);
t[ls].upd=t[rs].upd=t[o].upd;
t[o].upd=-1;
}
}
inline void dfs1(int u) {
sz[u]=1;
for(unsigned i=0;iint v=G[u][i];
if(v==fa[u])
continue;
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
sz[u]+=sz[v];
if(!son[u]||sz[son[u]]inline void dfs2(int u,int p) {
id[u]=++dfs_clock;
tp[u]=p;
if(!son[u])
return;
dfs2(son[u],p);
for(unsigned i=0;iint v=G[u][i];
if(v==fa[u]||v==son[u])
continue;
dfs2(v,v);
}
}
inline void build(int o,int l,int r) {
t[o].init();
if(l==r)
return;
int mid=(l+r)>>1;
build(lson);
build(rson);
pushup(o);
}
inline void upset(int o,int l,int r,int ql,int qr,int s) {
pushdown(o,l,r);
if(l==ql&&r==qr) {
//cout<
t[o].sum=s*(r-l+1);
t[o].upd=s;
//cout<
return;
}
int mid=(l+r)>>1;
if(qr<=mid)
upset(lson,ql,qr,s);
else if(ql>mid)
upset(rson,ql,qr,s);
else {
upset(lson,ql,mid,s);
upset(rson,mid+1,qr,s);
}
pushup(o);
}
inline void subset1(int x) {
upset(1,1,n,id[x],id[x]+sz[x]-1,0);
}
inline void subset2(int x) {
//cout<
while(tp[x]!=1) {
//cout<
upset(1,1,n,id[tp[x]],id[x],1);
x=fa[tp[x]];
}
//cout<
upset(1,1,n,1,id[x],1);
}
int main() {
rd(n);
for(int i=2,x;i<=n;i++) {
rd(x);x++;
G[x].push_back(i);
G[i].push_back(x);
}
dep[1]=1;fa[1]=-1;
dfs1(1);
dfs2(1,1);
build(1,1,n);
/*for(int i=1;i<=n;i++)
cout<rd(q);
while(q--) {
char s[100];int x;
scanf("%s",s+1);rd(x);x++;
int res=t[1].sum;
if(s[1]=='u') {
subset1(x);//cout<
printf("%d
",abs(res-t[1].sum));
}
else {
subset2(x);//cout<
printf("%d
",abs(res-t[1].sum));
}
}
}