[Jizhongトレーニング] 2019年8月10日[省]アナログTJの選択されたグループ

序文

  • 農業の質問のコード......

T1

説明

  • 所与\(N(\ [2,10における ^ 5])\) 木、ポイント\(M(≦10 ^ 5)\)回の問い合わせ、各クエリ点二つの非同一があるのすべてが最も時間のかかるを求めて、この時点までの二点(同じフライトで時間の側面のかかる一つのユニット、すべてのポイントを取るために)のいずれかを来てみましょう。

    SolutionⅠ

  • LCT:この質問は、シンプルで自然な方法があります!
  • 我々は2つのインタロゲーション・LCTは、前記切断側の中間点を取得し使用する; 2つの呼掛け点は、その後makeroot、クエリツリーのそれぞれの最大深さです。
  • 仮想エッジが持ち出しの最大深さは数える必要があることに注意してください。そして、これらのものの維持管理は、セット/マルチセットを戦わなければなりません。LCTはフリップ操作を持っているので、彼らが直接交換することができたときに、我々は正の値と最大抗フリップの最大の深さを必要としています。

    SolutionⅡ

  • この問題は、少しだけ...... noip2018D3T3好きではないのですか?
  • はい!私たちは、掛けることができます!二つの配列を乗算して配置された\(アップ[I] [J] \)、\ (DW [I] [J] \)示す\を(私は\)その最初に\(2 ^ j個の\)祖先チェーンサブツリーのすべての点で息子が(しない(私は\)\と略記この類似した以下のため、アカウントにサブツリーを\(\)する\(Bは\) そのに来て\ (2 ^ j個\)祖先/歩く\(私は\)の答え。
  • 求めて\((x、y)は\ ) (希望インペリアル\(deep_x≥deep_y\) )、我々は見つける\(Z = LCA(X、Y)\) および見つけ\((x、y)は\ ) 中間点\(Z1 \) その後、回答は、6つの部分に分割される計算:. 1 \(x \)全てが行ったサブツリー\(X \) ; 2 \(Xの\)する(Z1を\)\全てが行った\を(X \ ) ; .. 3 \(Z1 \)する\(Z \)全て来る\(Y \) ; .. 4 \(Y \)する(Z \)\全てが来る\(Y \) ; 5. \を( Yが\)サブツリー全体ウォークである\(Y \) ; .. 6 \(Z \)サブツリーと外側のすべての点のための\(Z \) 除く息子(X \ \)、\ (Y \)先祖は)すべてのサブツリーを行ってきました\(Y \)

    コード
  • これは、倍増LCTよりも難しいヒット......
#include <cstdio>
#include <vector> 
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;

const int N=11e4;
int n,x,y,z,f[N][17],dep[N],in[N],out[N],dw[N][17],up[N][17],m,todep,z1,z2,ans;
vector<int> e[N];

void MAX(int&x,int y) {if(x<y) x=y;}
int max(int x,int y) {return x>y?x:y;}

void dfs(int x)
{
    vector<int>::iterator it; int y;
    for(it=e[x].begin(); it!=e[x].end(); it++) 
        if((y=*it)^f[x][0])
        {
            f[y][0]=x, dep[y]=dep[x]+1, dfs(y);
            MAX(dw[y][0],in[x]+1);
            MAX(up[y][0],in[x]);
            MAX(in[x],in[y]+1);
        }
    int g=0;
    for(it--; 233; it--)
    {
        if((y=*it)^f[x][0])
        {
            MAX(dw[y][0],g+1);
            MAX(up[y][0],g);
            MAX(g,in[y]+1);
        }
        if(it==e[x].begin()) break;
    }
}

int lca(int x,int y)
{
    fd(i,16,0) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    if(x==y) return x;
    fd(i,16,0) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

int main()
{
    scanf("%d",&n);
    fo(i,1,n-1)
    {
        scanf("%d%d",&x,&y);
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(dep[1]=1);
    fo(i,1,16)
        fo(x,1,n)
        {
            if(!(f[x][i]=f[y=f[x][i-1]][i-1])) continue;
            dw[x][i]=max(dw[x][i-1],dw[y][i-1]+(1<<i-1));
            up[x][i]=max(up[x][i-1]+(1<<i-1),up[y][i-1]);
        }
    fo(x,1,n)
    {
        z1=x;
        fd(i,16,0)
            if(f[z1][i])
            {
                MAX(out[x],dw[z1][i]+dep[x]-dep[z1]);
                z1=f[z1][i];
            }
    }
    for(scanf("%d",&m); m--;)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(dep[x]<dep[y]) swap(x,y);
        z=lca(x,y);
        todep=(z^y?dep[x]-(dep[x]+dep[y]-2*dep[z]-1>>1):(dep[x]+dep[y]>>1)+1);
        z1=x, ans=max(in[x],out[z]+dep[y]-dep[z]);
        fd(i,16,0)
            if(dep[f[z1][i]]>=todep) 
            {
                MAX(ans,dw[z1][i]+dep[x]-dep[z1]);
                z1=f[z1][i];
            }
        fd(i,16,0)
            if(dep[f[z1][i]]>dep[z])
            {
                MAX(ans,up[z1][i]+dep[f[z1][i]]+dep[y]-2*dep[z]);
                z1=f[z1][i];
            }
        x=z1;
        if(z^y)
        {
            MAX(ans,in[z1=y]);
            fd(i,16,0)
                if(dep[f[z1][i]]>dep[z])
                {
                    MAX(ans,dw[z1][i]+dep[y]-dep[z1]);
                    z1=f[z1][i];
                }
            for(vector<int>::iterator it=e[z].begin(); it!=e[z].end(); it++)
                if((z2=*it)^f[z][0]&&z2^x&&z2^z1)
                    MAX(ans,in[z2]+1+dep[y]-dep[z]);
        }
        else    MAX(ans,up[z1][0]);
        printf("%d\n",ans);
    }
}

T2

説明

  • 図1は、ルートに1ポイント付与([2,10にN(\ \を ^ 5])\) ツリーの点、およびその順序に従って必要DFN配置が(各ポイントを息子に与えます序文)。そこ(m個\)\操作を、3つの操作があります。
  • 操作Ⅰ2つのインタロゲーション点間の距離。
  • 操作Ⅱ:HおよびV与えられ、そしてV父親の側を切断し、それはその最初の祖先hに接続されています。
  • 操作Ⅲ:シーク深\(K \)最大点DFNの点の配列。

    SolutionⅠ

  • この方法は、CC dalaoによって提供されます。
  • ツリー形式Ⅰの操作およびメンテナンスにLCT、スプレー木を開くDFN元の順序を維持します。移動したいどのように多くのポイントを知ることは容易であるので、我々は、LCTのメンテナンスサブツリーのサイズを使用することができるので、操作Ⅱを解決し、Ⅲ操作、それは半分スプレイすることができ、右に彼の息子を取るようにしてみてください。

    SolutionⅡ

  • ETT(スプレイでのオイラーツアーツリーカッコ内の秩序を維持します)。
  • 我々は、元のツリーの各点は、2つのブラケットに分割され、左括弧、右括弧-1であり、良好な特性がある:元のツリー内の点の深さは、その対応する左括弧と同等ですそして、プレフィックス。この場合、我々は、元のツリー、括弧内の参照符号を対応する各点を記録し、それぞれが広がりその価値、及びサブツリーサブツリーの最大値/最小値と接頭ドット記録します。
  • Ⅰ操作は、あなたはそれらの深さが最小と2つの左括弧の間LCAプレフィックスは確かであることを、左括弧に対応する2点を見つけることができます。
  • VⅡ操作直接サブツリーを行うために、クエリが正確V、深さをDFN前(プレフィックス)H番目の祖先クエリー配列と等価である\(deep_v時間の\)最後の点です。これは半分スプレーすることが、また、右の息子を取るしようとすることができます。
  • 完全な奥行き分離された二つの木にⅢ操作\(K \)は、最後の点です。

    コード
  • 次のコードは、スプレイ-半分に私が操作をストレッチしませんでした。そうで共有されていない均等にやって\(O((NM +)\ log_2nを)\)が、最悪の\(O(nm)を\) しかし、考慮に話題怠惰な人々を取って、ランダムなデータのほとんどが、半分のストレッチはもっとゆっくり実行します。
#include <cstdio>
#define A son[x][0]
#define B son[x][1]
#define A1 son[y][0]
#define B1 son[y][1]
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

const int N=21e4;
int n,m,l,a[N],pa[N],to[N],ne[N],la[N],ti,p[N],v[N],fa[N],son[N][2],s[N],mx[N],mn[N],rt;

void ins(int x,int y) {static int tot=0; pa[to[++tot]=y]=x, ne[tot]=la[x], la[x]=tot;}

void dfs(int x)
{
    v[p[++ti]=(x<<1)-1]=1;
    for(int i=la[x],y; y=to[i]; i=ne[i]) dfs(y);
    v[p[++ti]=x<<1]=-1;
}

int max(const int&x,const int&y) {return x>y?x:y;}
int min(const int&x,const int&y) {return x<y?x:y;}
bool so(int x) {return son[fa[x]][1]==x;}
void link(int y,int x,bool k)
{
    if(y) son[y][k]=x;
    if(x) fa[x]=y;
}
void up(int x)
{
    s[x]=s[A]+v[x]+s[B];
    mx[x]=max(mx[A],s[A]+v[x]+max(mx[B],0));
    mn[x]=min(mn[A],s[A]+v[x]+min(mn[B],0));
}
int build(int l,int r)
{
    int mid=l+r>>1,&x=p[mid];
    if(l<mid) link(x,build(l,mid-1),0);
    if(mid<r) link(x,build(mid+1,r),1);
    up(x);
    return x;
}
void rot(int x)
{
    if(!x) return;
    int y=fa[x],z=fa[y],k=so(x),b=son[x][!k];
    link(y,b,k);
    link(z,x,so(y));
    link(x,y,!k);
    up(y), up(x);
}
void splay(int x,int y) {for(int f=fa[x]; f^y; rot(x),f=fa[x]) rot(fa[f]^y?so(x)==so(f)?f:x:0);}
int MIN(int&x,const int&y) {if(x>y) x=y;}
int dis(int x,int y)
{
    x=(x<<1)-1, y=(y<<1)-1;
    int dx,dy,dl;
    splay(x,0), dx=s[A]+v[x];
    splay(y,0), dy=s[A1]+v[y];
    splay(x,y), rt=y;
    dl=min(dx,dy);
    MIN(dl, A1==x ? s[A]+v[x]+mn[B] : s[A1]+v[y]+mn[A] );
    return dx+dy-2*dl;
}
int find(int x,int k)
{
    while(233)
    {
        int k1=k-s[A]-v[x];
        if(mn[B]<=k1&&k1<=mx[B]) {k=k1,x=B; continue;}
        if(s[A]+v[x]==k) return x&1?x+1>>1:pa[x>>1];
        x=A;
    }
}
int pre(int x) {splay(x,0); for(x=A;B;x=B); return x;}
int nxt(int x) {splay(x,0); for(x=B;A;x=A); return x;}
void move(int u,int h)
{
    int x=(u<<1)-1,L,R,t;
    splay(x,0);
    pa[u]=find(A,s[A]+v[x]-h);
    L=pre(x), R=nxt(x+1);
    splay(L,0), splay(R,L);
    t=son[R][0], son[R][0]=0;
    up(R), up(L);
    L=pre(R=pa[u]<<1);
    splay(L,0), splay(R,L);
    link(R,t,0);
    up(R), up(rt=L);
}

int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)
    {
        scanf("%d",&l);
        fo(j,1,l) scanf("%d",&a[j]), pa[a[j]]=i;
        while(l) ins(i,a[l--]);
    }
    dfs(1);
    mn[0]=N, mx[0]=-N;
    rt=build(1,ti);
    int tp,x,y;
    while(m--)
    {
        scanf("%d%d",&tp,&x);
        switch(tp)
        {
            case 1:scanf("%d",&y); 
            printf("%d\n",x^y?dis(x,y):0);
            break;
            
            case 2:scanf("%d",&y);
            move(x,y);
            break;
            
            case 3:printf("%d\n",find(rt,x+1));
            break;
        }
    }
}

T3

説明

  • 説明マップ上、あまりにも複雑です。
    ここに画像を挿入説明
  • データの30%を、N、M <= 5、データセットの数<= 7
  • データの100%に、2 <= N、M <= 50、データセットの数<= 17

    溶液

  • DP!
  • 水まで垂直方向ながら、唯一の左上、右上、左下、右下のトランク参照することにより前処理することができます。以下に示すように、それは、次に3つのケースに分けてもよいです。
    ここに画像を挿入説明
  • もちろん、このの右または左があり、数字を得ることができる90°回転させます。簡単にするために、私は元の行、列の交換をしています。
    ここに画像を挿入説明
    ここに画像を挿入説明
  • 図は、左第三(または垂直)に見出すことができる第二の図を反転が得られます。

    コード

#include <cstdio>
#include <cstring>
#include <algorithm>
#define max(x,y) (x>y?x:y)
#define C(a) memset(a,0,sizeof a);
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

const int N=55;
int n,a[N][N],u[N][N],l[N][N],d[N][N],r[N][N];
int ul[N][N],ur[N][N],dl[N][N],dr[N][N],ud[N][N],ans;
char s[N][N];

inline void MAX(int&x,const int&y) {if(x<y)x=y;}

void init(bool k)
{
    fo(i,1,n)
    {
        fo(j,1,n)
        {
            u[i][j]=max(u[i-1][j],a[i][j]);
            l[i][j]=max(l[i][j-1],a[i][j]);
            ul[i][j]=max(ul[i][j-1]+u[i][j],ul[i-1][j]+l[i][j]);
        }
        fd(j,n,1)
        {
            r[i][j]=max(r[i][j+1],a[i][j]);
            ur[i][j]=max(ur[i][j+1]+u[i][j],ur[i-1][j]+r[i][j]);
        }
    }
    fd(i,n,1)
    {
        fo(j,1,n) 
        {
            d[i][j]=max(d[i+1][j],a[i][j]);
            dl[i][j]=max(dl[i][j-1]+d[i][j],dl[i+1][j]+l[i][j]);
        }
        fd(j,n,1) dr[i][j]=max(dr[i][j+1]+d[i][j],dr[i+1][j]+r[i][j]);
    }
    if(k) return;
    fo(j,1,n)
    {
        ud[j][j]=0;
        fo(i,0,n) MAX(ud[j][j],u[i][j]+d[i+1][j]);
    }
    fo(i,1,n-1) fo(j,i+1,n) ud[i][j]=ud[i][j-1]+ud[j][j];
}

void calc1()
{
    fo(i,0,n)
        fo(j,0,n)
            fo(k,0,n)
                fo(l,k+1,n+1)
                    MAX(ans,ul[i][k]+ur[j][l]+ud[k+1][l-1]+dl[i+1][k]+dr[j+1][l]);
}

void calc2()
{
    fo(i,0,n)
        fo(j,1,i)
            fo(k,0,n)
                fo(l,1,k)
                    MAX(ans,ul[i][l-1]+ur[j-1][l]+dl[i+1][k]+dr[j][k+1]);
}

int main()
{
    while(~scanf("%d",&n))
    {
        C(u) C(l) C(d) C(r)
        C(ul) C(ur) C(dl) C(dr) C(ud)
        fo(i,1,n)
        {
            scanf("%s",s[i]+1);
            fo(j,1,n) a[i][j]=s[i][j]^48;
        }
        
        ans=0;
        init(0), calc1();
        
        fo(i,1,n-1) fo(j,i+1,n) swap(a[i][j],a[j][i]);
        init(0), calc1();
        
        calc2();
        
        fo(i,1,n) fo(j,1,n/2) swap(a[i][j],a[i][n-j+1]);
        init(1), calc2();
        
        printf("%d\n",ans);
    }
}

おすすめ

転載: www.cnblogs.com/Iking123/p/11333798.html