(13)noip2019トレーニングテストイベントを[ファイルを塗りつぶし]

問題A:妖精

制限時間:1000msのメモリ制限:256メガバイト

説明

図M(ループフリー)に縁なしに、n個の点が与えられ、エッジの除去は、図1の二部グラフにQを可能図形の一方の側から除去することができます。

入力

ライン1は、2つの整数N、M、それぞれ、点とエッジを含みます。

2〜M + 1つのラインの各2つの数のx、yは、点X、Yを連結するエッジがあることを示します。

出力

エッジの数を表す2つの整数の最初の行は削除することができます。

出力側の昇順で次の行には、番号を削除することができます。

Sample Input
4 4
1 2
1 3
2 4
3 4
Sample Output
4
1 2 3 4

ヒント

データの10%、N、M <= 10

データの40%、N、M <= 1000

70データの%、N、M <= 100000

100%のデータ、nは、M <= 2000000

溶液

我々は、奇数とエバ二つの配列、奇数[U]奇数環は、前夜欧州リングは、いくつかの(U、FA)経過したことを示し、このエッジを使用します

奇数リングがない場合に全体のグラフの、そしてちょうど答えであるエッジを選択。

奇数リングの片側のみが、その後、ヨーロッパのすべてがリング上の奇妙なリングがない場合には答えです。

複数の奇数の環が存在する場合、これだけエッジリング奇数の(U、FA)と奇数の総数後、その条件を満たすようにヨーロッパを通じて同じ環を鳴りません。

どのように奇妙なリングと欧州中央統計はありますか?差分統計を考えてみましょう:

示すように、DFSはまず、ない側のDFS木、私たちの統計学的な差ではそれらのために、ツリー全体図を走りました。

その後、統計が取得します。コードは次のように実装されています。

#include<bits/stdc++.h>
using namespace std;
struct qwq{
    int v;
    int nxt;
    int id;
}edge[4000001];
int head[4000001];
int cnt=-1;
void add(int u,int v,int id){
    edge[++cnt].nxt=head[u];
    edge[cnt].v=v;
    edge[cnt].id=id;
    head[u]=cnt;
}
int tmp1[4000001],tmp2[4000001];
int odd[4000001],eve[4000001];
int fa[4000001],dep[4000001];
bool mk[4000001];
int ans;
int tmp;
int n,m;
void dfs(int u){
    for(int i=head[u];~i;i=edge[i].nxt){
        int v=edge[i].v;
        if(!dep[v]){
            fa[v]=u,dep[v]=dep[u]+1;
            mk[edge[i].id>m?edge[i].id-m:edge[i].id]=true;
            dfs(v);
            odd[u]+=odd[v],eve[u]+=eve[v];
        }
        else if(v!=fa[u]&&dep[v]<dep[u]){
            if((dep[u]-dep[v])%2==1){
                tmp2[u]++,tmp2[v]--;
            }
            else {
                ans++;
                tmp1[u]++,tmp1[v]--;
                tmp=edge[i].id>m?edge[i].id-m:edge[i].id;
            }
        }
    }
    odd[u]+=tmp1[u],eve[u]+=tmp2[u];
}
int tot;
int anss[4000001];
struct ed{
    int u,v;
}e[4000001];
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v,i),add(v,u,i+m);
        e[i].u=u,e[i].v=v;
    }
    for(int i=1;i<=n;++i){
        if(!dep[i]){
            dep[i]=1;
            dfs(i);
        }
    }
    if(ans==0){
        printf("%d\n",m);
        for(int i=1;i<=m;++i)printf("%d ",i);
        return 0;
    }
    if(ans==1){
        anss[++tot]=tmp;
    }
    //for(int i=1;i<=n;++i){
    //  cout<<odd[i]<<" "<<eve[i]<<endl;
    //}
    for(int i=1;i<=m;++i){
        if(mk[i]){
            int u;
            if(fa[e[i].v]==e[i].u)u=e[i].v;
            if(fa[e[i].u]==e[i].v)u=e[i].u;
            if(odd[u]==ans&&eve[u]==0){
                anss[++tot]=i;
            }
        }
    }
    printf("%d\n",tot);
    sort(anss+1,anss+tot+1);
    for(int i=1;i<=tot;++i)printf("%d ",anss[i]);
}

問題B:フォーニ

制限時間:3000ミリ秒のメモリ制限:512メガバイト

説明

あなたはP. n個の要素の順序があり、初期の長さlenがあり、文字列Sを与えます

次に、m個の動作は、3つのタイプ、すなわち、存在します。

  1. 文字列の前に追加されます

  2. 要素Pの値を変更します

  3. 全てのi∈[L、R]を求め、そのようなS [LP [I] + 1..L] iが辞書最小(複数の出力の最小値I、Lは、現在の文字列の長さである)こと

(力によって)

入力

入力コモンラインm + 3。

三つの整数N、M、LENの最初の行。

第2行動の初期文字列S.

第3行数n、P [1] ... P [N]。

続いて行をM、各列は、演算を表します。

  1. 最も、前記Qのlastans問い合わせへの最後の答えに前の位置で最初のI C(C XOR lastans)+1小文字の文字列、最初に0を加えます。

  2. C xはP [x]はPOSに修正される陽

  3. Q LRは、[l..r]間隔を依頼する提示します。

出力

出力の各行についてQは答えを示すために尋ねました。

Sample Input
3 3 5
horni
3 2 5
I 15
C 1 6
Q 1 3
Sample Output
3

ヒント

データを、1 <= N <= 100,1 <= M <= 100,1 <= LEN <= 100,1 <= P [i]は<= LENの10%。

データを、1 <= N <= 500000,1 <= M <= 800000,1 <= LEN <= 100000,1 <= P [i]は<= LENの100%まで。

I動作、0 <= C XOR lastans <= 25。

操作C 1 <= xで<= N、1 <= POS <=現在の文字列の長さ。

Q動作1 <= L、R <= nです。

操作量1/5の操作の総数のI、C、Q動作の数、各操作の総数の約2/5。

溶液

ああ、接尾辞バランス木のボードに関する質問

私たちは、接尾辞のために長い間に短いから注文します

すぐに2つの辞書式サフィックスの大きさを決定する方法を考えてみましょう:

直接決定と異なる最初の文字の場合。

それ以外の場合は、2つの接尾語接尾サイズの判定。

それは長い間に短いから挿入されているので、そのサフィックスは良い取引されている必要があります

あなたは、新しいサフィックスを追加した場合、我々は(明らかに)前回のためのサフィックスを変更しない、それが直接挿入することができます

ツリー平衡ツリーのため、我々は、各ノードのValのLの値を設定し、R、ノードの重み=(L + R)/ 2

単調シーケンスの先行順増加を確保するために息子Lは左、Rは、L、ヴァル-1であります

バランスウェイトを達成するために、ツリー - この機能を満たすために、我々はバランスの取れたツリーを使用する必要があります。

だから我々はスケープゴートツリーを選択します。次に、範囲クエリのため、その上にセグメントツリーを設定します

#include<bits/stdc++.h>
using namespace std;
int read(){
    int num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}
struct node{
    double l,r,val;
    int ch[2];
    int siz;
}t[1000001];
int tot;
int rt;
char str[1000001];
const double alp=0.75;
void newnode(int x,double l,double r){
    t[x].l=l,t[x].r=r;
    t[x].val=(l+r)/2;
    t[x].siz=1,t[x].ch[0]=t[x].ch[1]=0;
}
void pushup(int x){
    t[x].siz=t[t[x].ch[0]].siz+t[t[x].ch[1]].siz+1;
}
bool isbad(int x){
    return t[t[x].ch[0]].siz>t[x].siz*alp||t[t[x].ch[1]].siz>t[x].siz*alp;
}
int que[1000001],L;
void dfs(int x){
    if(t[x].ch[0])dfs(t[x].ch[0]);
    que[++L]=x;
    if(t[x].ch[1])dfs(t[x].ch[1]);
}
int build(int l,int r,double x,double y){
    if(l>r)return 0;
    int mid=(l+r)/2;
    int k=que[mid];
    newnode(k,x,y);
    t[k].ch[0]=build(l,mid-1,x,t[k].val);
    t[k].ch[1]=build(mid+1,r,t[k].val,y);
    pushup(k);
    return k;
}
void rebuild(int &k){
    L=0;
    dfs(k);
    k=build(1,L,t[k].l,t[k].r);
}
int sta[1000001],top;
void Re(){
    for(int i=1;i<=top;++i){
        if(isbad(sta[i])){
            if(i==1)rebuild(rt);
            else {
                rebuild(t[sta[i-1]].ch[t[sta[i-1]].ch[1]==sta[i]]);
                pushup(sta[i-1]); 
            }
            return;
        }
    }
}
bool cmp(int x,int y){
    return str[x]==str[y]?t[x-1].val<t[y-1].val:str[x]<str[y];
}
void ins(int p){
    tot++;
    if(!rt){
        rt=tot;
        newnode(tot,1,1e18);
        return;
    }
    top=0;
    int k=rt;
    while(233){
        bool d=cmp(k,p);
        sta[++top]=k;
        t[k].siz++;
        if(!t[k].ch[d]){
            t[k].ch[d]=tot;
            if(d)newnode(tot,t[k].val,t[k].r);
            else newnode(tot,t[k].l,t[k].val);
            break;
        }
        k=t[k].ch[d];
    }
    Re();
}
int len[1000001];
int minn[4000001];
int pushup(int x,int y){
    return t[len[x]].val<=t[len[y]].val?x:y;
}
void Build(int o,int l,int r){
    if(l==r){
        minn[o]=l;
        return;
    }
    int mid=(l+r)/2;
    Build(o*2,l,mid);
    Build(o*2+1,mid+1,r);
    minn[o]=pushup(minn[o*2],minn[o*2+1]);
}
void update(int o,int l,int r,int pos){
    if(l==r){
        minn[o]=l;
        return;
    }
    int mid=(l+r)/2;
    if(pos<=mid)update(o*2,l,mid,pos);
    else update(o*2+1,mid+1,r,pos);
    minn[o]=pushup(minn[o*2],minn[o*2+1]); 
}
int query(int o,int l,int r,int L,int R){
    if(L<=l&&r<=R)return minn[o];
    int mid=(l+r)/2;
    if(R<=mid)return query(o*2,l,mid,L,R);
    if(mid<L)return query(o*2+1,mid+1,r,L,R);
    return pushup(query(o*2,l,mid,L,R),query(o*2+1,mid+1,r,L,R));
}
int main(){
    int n=read(),q=read(),m=read();
    scanf("%s",str+1);
    reverse(str+1,str+1+m);
    for(int i=1;i<=n;++i){
        len[i]=read();
    }
    for(int i=1;i<=m;++i)ins(i);
    Build(1,1,n);
    int lst=0;
    char opt[12];
    for(int i=1;i<=q;++i){
        scanf("%s",opt);
        if(opt[0]=='I'){
            int x=read();
            str[++m]='a'+(x^lst);
            ins(m);
        }
        if(opt[0]=='C'){
            int x=read(),pos=read();
            len[x]=pos;
            update(1,1,n,x);
        }
        if(opt[0]=='Q'){
            int l=read(),r=read();
            printf("%d\n",lst=query(1,1,n,l,r));
        }
    }
}

問題C:冬の風

制限時間:1000msのメモリ制限:512メガバイト

説明

ツリー内の各ノードのために、Nポイントツリーが与えられると、各点について、それが削除され、その後、森のいずれかは、父親が切断点を得ることができ、他の木に接続されましたフォレスト内のすべてのツリーの最大サイズの最小値を求めます。

入力

最初の行数n。

二つの整数xの2〜N + 1行、Y、(1 <= xと<= Y <= N)のうち、Yが(その後、yがルートであるx = 0の場合)は、xの父親である表わします。

出力

数のn行の合計は、i行目の答えは、私は省略点を表します。

Sample Input
10
0 1
1 2
1 3
1 4
2 5
2 6
3 7
4 8
4 9
5 10
Sample Output
3
4
5
5
5
9
9
9
9
9

ヒント

データの10%、N <= 10

データの30%、N <= 10 ^ 3

100%のデータ、N <= 10 ^ 5

溶液

GUGU区

おすすめ

転載: www.cnblogs.com/youddjxd/p/11425774.html