1. 程式人生 > >2017-10-27離線賽小結

2017-10-27離線賽小結

估分:280
實際分數:260
這次考得還過得去,終於逃脫了炸零魔咒,但最後一題線段樹敲錯Tle了20分還是很悲傷的
小C:你怎麼那麼low啊
是時候總結一下考試要幹些什麼了
1.敲模板(標頭檔案,讀入掛)
2.寫備註(long long double 檔名 乘法 除錯)
3.把檔名複製到freopen裡(一定要複製!)
4.敲暴力(以前第一題炸零的經歷告訴我,前面兩題一定要敲暴力)
5.想正解,同時要預估時間(主要針對第三題)
6.想不出正解時,果斷棄坑,開始往死裡切分(一般切到50+還是容易的)
這樣下來每場200+時有保證的
7.敲完後求記憶體,開始對拍
8.交之前 freopen 檔名 除錯

題解:
A:這種題一看就是先找規律,能不能貪心,然後想不到再想想二分
結果真的是二分
B: 一道神dp,也就是先暴力n重迴圈,然後瞎調調出來了加上一些字首和優化 這題暴力水不到分是最尷尬的
總結一下,這種計數的dp :先暴力n重迴圈,在字首和優化時間,在滾動優化記憶體
C:這道題用點時間還是能輕鬆水到80分的,比敲正解加除錯要容易得多(重點是切分之間可以互相對拍)

還是得說正解的,這題有兩種做法,都很值得學習:
1.二分答案+差分字首和
首先得想到答案是具有單調性的,就是趨向於一種滿足.滿足.不滿足的狀態(好像說反了)
然後根據二分出的答案篩選出不滿足(大於)答案的邊
這題的特性是隻用刪一條邊,那麼可以利用差分,標記這個點的lca,和左右兩點,在配合dfs棧,在O(n)的複雜度內求出這些邊的交邊,然後判斷是否能刪掉一條邊並得出答案
程式碼實現(卡過去的,倍增常數實在太大了):

#include<bits/stdc++.h>
using namespace std;
#define S 21
#define M 400005
int Fa[S][M],C[M],V[M],Val[M];
int dis[M],dep[M];
int dfsL[M],T;
struct node{int to,v;};
vector<node>edge[M];
struct NODE{int l,r,lca,v;}Q[M];
bool cmp(NODE a,NODE b){
    return a.v>b.v;
}
void dfs(int x,int f){
    Fa[0
][x]=f; dep[x]=dep[f]+1; dfsL[++T]=x; for(int i=0;i<(int)edge[x].size();i++){ int y=edge[x][i].to; if(y==f)continue; Val[y]=edge[x][i].v; dis[y]=dis[x]+Val[y]; dfs(y,x); } } void UP(int &x,int len){ for(int i=0;i<S;i++){ if(len&(1<<i)){ x=Fa[i][x]; } } } int LCA(int x,int y){ if(dep[x]>dep[y])swap(x,y); UP(y,dep[y]-dep[x]); if(x==y)return x; for(int i=S-1;i>=0;i--){ if(Fa[i][x]!=Fa[i][y]){ x=Fa[i][x]; y=Fa[i][y]; } } return Fa[0][x]; } bool flag; int k; int n,m; bool check(int res){ memset(C,0,sizeof(C)); k=0; int mx=0; for(int i=1;i<=m;i++){ if(Q[i].v<=res)continue; C[Q[i].l]++,C[Q[i].r]++; C[Q[i].lca]-=2; k++; mx=max(mx,Q[i].v-res); } for(int i=n;i>1;i--){ C[Fa[0][dfsL[i]]]+=C[dfsL[i]]; } for(int i=2;i<=n;i++)if(C[i]==k&&mx<=Val[i])return 1; return 0; } int main(){ int l=0,r=1e9,res=0; scanf("%d%d",&n,&m); for(int i=1;i<n;i++){ int x,y,v; scanf("%d%d%d",&x,&y,&v); edge[x].push_back((node){y,v}); edge[y].push_back((node){x,v}); } dfs(1,0); for(int i=1;i<=S-1;i++) for(int j=1;j<=n;j++) Fa[i][j]=Fa[i-1][Fa[i-1][j]]; for(int i=1;i<=m;i++){ scanf("%d%d",&Q[i].l,&Q[i].r); Q[i].lca=LCA(Q[i].l,Q[i].r); Q[i].v=dis[Q[i].l]+dis[Q[i].r]-2*dis[Q[i].lca]; } while(l<=r){ int mid=(l+r)>>1; if(check(mid)){ r=mid-1; res=mid; } else l=mid+1; } printf("%d\n",res); return 0; }

2.又是一種新的想法
把樹壓成一條鏈,然後得出答案:
首先得造出一條鏈
我們可以把最長的路徑抽出來,然後把非鏈上的點壓到鏈上
利用字首和字尾O(1)得出不經過被刪邊的路徑的最大值
以及最大路徑刪掉那條邊後的值
程式碼實現:

#include<bits/stdc++.h>
using namespace std;
const int S=20,M=300005;
int fa[S][M],dep[M],L[M],R[M],lca[M],Val[M],dis[M];
int Lmx[M],Rmx[M];
struct node{int to,v;};
vector<node>edge[M];
void ldfs(int x,int f){
    dep[x]=dep[f]+1;
    fa[0][x]=f;
    for(int i=0;i<(int)edge[x].size();i++){
        int y=edge[x][i].to;
        if(f==y)continue;
        dis[y]=dis[x]+edge[x][i].v;
        ldfs(y,x);
    }
}
void up(int &x,int len){
    for(int i=0;i<S;i++){
        if(len&(1<<i))x=fa[i][x];
    }
}
int LCA(int x,int y){
    if(dep[x]>dep[y])swap(x,y);
    up(y,dep[y]-dep[x]);
    if(x==y)return x;
    for(int i=S-1;i>=0;i--){
        if(fa[i][x]!=fa[i][y]){
            x=fa[i][x];
            y=fa[i][y];
        }
    }
    return fa[0][x];
}
bool mark[M];
int Col[M],T[M],Cost[M];
int cnt;
void mk(int x,int y){
    if(dep[x]>dep[y])swap(x,y);
    int len=dep[y]-dep[x];
    mark[x]=1,mark[y]=1;
    for(int i=1;i<=len;i++){
        y=fa[0][y];
        mark[y]=1;
    }
    if(x==y)return;
    while(x!=y){
        x=fa[0][x];
        y=fa[0][y];
        mark[x]=1;
        mark[y]=1;
    }
}
void mdfs(int x,int f){
    T[++cnt]=x;
    for(int i=0;i<(int)edge[x].size();i++){
        int y=edge[x][i].to;
        if(mark[y]==0||y==f)continue;
        Cost[cnt]=edge[x][i].v;
        mdfs(y,x);
    }
}
void rdfs(int x,int f,int v){
    Col[x]=v;
    for(int i=0;i<(int)edge[x].size();i++){
        int y=edge[x][i].to;
        if(y==f||mark[y])continue;
        rdfs(y,x,v);
    }
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);

    for(int i=1;i<n;i++){
        int x,y,v;
        scanf("%d%d%d",&x,&y,&v);
        edge[x].push_back((node){y,v});
        edge[y].push_back((node){x,v});
    }

    ldfs(1,0);

    int mx=0,id=0;
    for(int i=1;i<S;i++)for(int j=1;j<=n;j++)fa[i][j]=fa[i-1][fa[i-1][j]];
    for(int i=1;i<=m;i++){
        scanf("%d %d",&L[i],&R[i]);
        lca[i]=LCA(L[i],R[i]);
        Val[i]=dis[L[i]]+dis[R[i]]-2*dis[lca[i]]; 
        if(Val[i]>mx){
            mx=Val[i];
            id=i;
        }
    }

    int ans=-1;
    mk(L[id],R[id]);
    mdfs(L[id],0);//抽直徑

    for(int i=1;i<=cnt;i++)rdfs(T[i],0,i);

    for(int i=1;i<=m;i++){
        if(i==id)continue;
        int l=L[i],r=R[i];
        if(Col[l]>Col[r])swap(l,r);
        if(Lmx[Col[r]]<Val[i])Lmx[Col[r]]=Val[i];
        if(Rmx[Col[l]]<Val[i])Rmx[Col[l]]=Val[i];
    }

    for(int i=1;i<cnt;i++)if(Lmx[i+1]<Lmx[i])Lmx[i+1]=Lmx[i];
    for(int i=cnt;i>1;i--)if(Rmx[i]>Rmx[i-1])Rmx[i-1]=Rmx[i];

    for(int i=1;i<cnt;i++){
        int val=max(Lmx[i],Rmx[i+1]);
        val=max(val,mx-Cost[i]);
        if(ans>val||ans==-1)ans=val;
    } 

    if(ans==-1)puts("0");
    else printf("%d\n",ans);
    return 0; 
}