[2018多省省隊聯測] Day 1 解題報告

T1 一雙木棋chess
這裏寫圖片描述
n,m<=10
Alice想最大化差值,Bob想最小化差值。發現棋子呈階梯狀排布,總狀態數階梯狀排布,階乘複雜度。
哈希記錄狀態後爆搜,記錄當前是誰的回合,按照自身的決策去最大化/最小化價值。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int base=15;
const ll INF=2e18;

map<ll,int>ma;
int st[15],A[15][15],B[15][15],n,m;

ll range;
ll gethash(){
    ll ans=0;
    for(int i=1;i<=n;i++)ans=ans*base+st[i];
    return ans;
}

ll dfs(ll S,int ty){
    if(ma[S])return ma[S];
    if(S==range)return 0;
    ll ans=ty==0?-INF:INF;
    for(int i=1;i<=n;i++){
        if(st[i]<st[i-1]){
            st[i]++;
            if(ty==0)ans=max(ans,dfs(gethash(),ty^1)+A[i][st[i]]);
            else ans=min(ans,dfs(gethash(),ty^1)-B[i][st[i]]);
            st[i]--;
        }
    }
    return ma[S]=ans;
}

int main(){
//  freopen("chess.in","r",stdin);
//  freopen("chess.out","w",stdout);
    memset(st,0,sizeof(st));
    scanf("%d%d",&n,&m);st[0]=m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&A[i][j]);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&B[i][j]);
        }
    }
    range=0;
    for(int i=1;i<=n;i++)range=range*base+m;
    cout<<dfs(0,0);
}

T2.IIIDX
這裏寫圖片描述

讀題題意即 有一些形態相近的樹組成的森林,樹的形態給定,每個節點從給定權值中賦予一個權值,使得每棵樹都是一個小根堆,並且要求字典序最大的方案。

把每棵樹根連向0號節點形成一棵樹。很直接的思路,排序後,對於同層次的樹,右邊的填小的留出位置給左邊。左邊填可控區間內最小的。
但是題目中有一個很奇怪的條件就是,數值可能會相同。。遇到奇怪的條件想一下反例。發現這樣做在有相同時是合法的而不是最優的。
我們考慮一個排好序的序列,從中找出一個點填入,使這個點的值儘量大。提前就要預留好他子樹中的位置,在有位置的情況下,找最左邊的(從大到小排序)。然後他右邊的節點都少了這些點的位置。對於一堆權值相同的節點,爲了保證正確性,我們取這裏面最右邊的節點。
這個操作是可以用線段樹實現的。
對於他父親已經預留了位置,在查詢第一個孩子時要把這個操作還原回去。
線段樹葉子記錄每個節點左邊包括自身還剩幾個空節點。這個具有單調性。
取一個min在線段樹上二分就可以得到答案、

#include<bits/stdc++.h>
using namespace std;

const int MAXN=5e5+5;

struct edge{
    int to,next;
}e[MAXN<<1];

int head[MAXN],cnt=0;
inline void add(int u,int v){e[++cnt]=(edge){v,head[u]},head[u]=cnt;}

inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

bool cmp(int a,int b){
    return a>b;
}

int size[MAXN],d[MAXN],after[MAXN],fa[MAXN],output[MAXN],n;
double kk;

void dfs(int u){
    size[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        dfs(v);
        size[u]+=size[v];
    }
}

struct xds{
    #define lson (o<<1)
    #define rson (o<<1|1)
    int minv[MAXN<<2],lzt[MAXN<<2];
    inline void pushup(int o){
        minv[o]=min(minv[lson],minv[rson]);
    }
    inline void pushdown(int o){
        if(lzt[o]){
            lzt[lson]+=lzt[o];lzt[rson]+=lzt[o];
            minv[lson]+=lzt[o];minv[rson]+=lzt[o]; 
            lzt[o]=0;
        }
    }
    inline void build(int o,int l,int r){
        if(l==r){minv[o]=l;return;}
        int mid=l+r>>1;
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(o);
    }
    inline void change(int o,int l,int r,int ql,int qr,int val){
        if(ql<=l&&qr>=r){minv[o]+=val;lzt[o]+=val;return;}
        int mid=l+r>>1;
        pushdown(o);
        if(ql<=mid)change(lson,l,mid,ql,qr,val);
        if(qr>=mid)change(rson,mid+1,r,ql,qr,val);
        pushup(o);
    }
    inline int find(int o,int l,int r,int k){
        if(l==r){return minv[o]>=k?l:l+1;}
        pushdown(o);
        int mid=l+r>>1;
        if(k>minv[rson])return find(rson,mid+1,r,k);
        return find(lson,l,mid,k);
    }
    #undef lson
    #undef rson
}T;

int main(){
//  freopen("iiidx.in","r",stdin);
//  freopen("iiidx.out","w",stdout);
    scanf("%d%lf",&n,&kk);
    for(int i=1;i<=n;i++){
        d[i]=read();
    }
    for(int i=n;i>=1;i--){
        int p=int(i/kk);
        fa[i]=p;
        add(p,i);
    }
    sort(d+1,d+1+n,cmp);
    for(int i=n;i>=1;i--)if(d[i]==d[i+1])after[i]=after[i+1]+1; else after[i]=0; 
    T.build(1,1,n);
    dfs(0);
//  for(int i=1;i<=n;i++)cout<<fa[i]<<" "<<size[i]<<" "<<after[i]<<endl;
    for(int i=1;i<=n;i++){
        if(fa[i]&&fa[i]!=fa[i-1])T.change(1,1,n,output[fa[i]],n,size[fa[i]]-1);
        int p=T.find(1,1,n,size[i]);
        p+=after[p];after[p]++;p-=after[p]-1;
        output[i]=p;
        T.change(1,1,n,p,n,-size[i]);
    }
    for(int i=1;i<=n;i++)printf("%d ",d[output[i]]);
    puts("");
    return 0;
}

T3 祕密襲擊coat
這裏寫圖片描述
標算不會,有一種優美的暴力。
題意爲 給一棵樹,從中選出所有不相同的元素個數大於k的連通塊,求這些連通塊中第k大元素權值和。
答案對64123取模。

我們考慮分別計算每個點的答案。
如果這個點在塊中爲第k大的連通塊有v個。對答案的貢獻爲vs。
問題轉化爲求出每個點是第k大元素的連通塊個數。
枚舉每個點作爲根。
如果這個點rank<k 可以直接剪掉。
否則統計答案。
因爲連通塊的信息是跨子樹的,所以我們不能像通常一下子樹上傳。還要從父節點下傳信息,再子樹更新信息。
如果下傳時他碰到了一個權值比他大的節點v,v與root所在連通塊中,root爲第k大的方案數等於 root與v的father連通塊中,root爲第k-1大的方案數。若v權值比root小則爲k大的方案數。
子節點信息上傳更新父節點計算答案即可。這樣在點的權值均不相同時顯然是對的。
若點的權值相同,我們可以強行按標號順序區分權值大小,其餘做法同上。

複雜度分析:
只有 nk 個點 每個點作爲根進行 dfs 。每次dfs遍歷 n 個點,每個點上傳加下傳複雜度O(k)
整體複雜度 O(n2knk2) 二次函數當 k 取得 n/2 時複雜度最差爲 O(n3/2n3/4)=O(n3/4)
因爲良心出題人並沒有卡,所以可以A。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll; 
const int MAXN=2000;
const int MOD=64123;
ll f[MAXN][MAXN];

struct edge{
    int to,next;
}e[MAXN<<1];
int head[MAXN],val[MAXN],cnt=0,V,n,m,k,w;
inline void add(int u,int v){
    e[++cnt]=(edge){v,head[u]},head[u]=cnt;
    e[++cnt]=(edge){u,head[v]},head[v]=cnt;
}

inline ll ADD(ll x,ll y){
    x+=y;if(x>=MOD)x-=MOD;
    return x;
}

void dfs(int u,int fa){
    if(val[u]>val[V]||(val[u]==val[V]&&u>V))for(int i=1;i<=k;i++)f[u][i]=f[fa][i-1];
    else for(int i=1;i<=k;i++)f[u][i]=f[fa][i];
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa)continue;
        dfs(v,u);
        for(int j=1;j<=n;j++)f[u][j]=ADD(f[u][j],f[v][j]);
    }

}

ll ans=0;

void calc(int p){
    //memset(f,0,sizeof())
    int tim=1;
    for(int i=1;i<=n;i++)if(val[i]>val[p]||(val[i]==val[p]&&i>p))tim++;
    if(tim<k)return;
    V=p;
    for(int i=0;i<=n;i++)f[p][i]=0;
    f[p][1]=1;
    for(int i=head[p];i;i=e[i].next){
        dfs(e[i].to,p);
        for(int j=1;j<=n;j++)f[p][j]=ADD(f[p][j],f[e[i].to][j]); 
    }
    //cout<<f[p][3]<<" "<<p<<endl;
    ans+=val[p]*f[p][k];
}

int main(){
    //freopen("coat.in","r",stdin);
    //freopen("coat.out","w",stdout);
    scanf("%d%d%d",&n,&k,&w);   
    for(int i=1;i<=n;i++){
        scanf("%d",&val[i]);
    }
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    for(int i=1;i<=n;i++){
        calc(i);
    }
    cout<<ans%MOD<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章