牛客練習賽63(C二分套三分 D 二維dp F nim博弈推論 樹上主席樹)

題目鏈接

C-牛牛的揠苗助長

二分天數然後三分高度check即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

const int N=1e5+10;
ll a[N],n,b[N];
ll run(ll mid)
{
    ll ans=0;
    rep(i,1,n) ans+=abs(b[i]-mid);
    return ans;
}
int cal(ll mid)
{
    ll d=mid%n;
    for(ll i=1;i<=n;++i) {
        b[i]=a[i]+mid/n;
        if(d>=i) b[i]++;
    }


    ll l=b[1],r=b[1];
    rep(i,1,n) l=min(l,b[i]),r=max(r,b[i]);


    while(l+10<r)
    {
        ll m1=l+r>>1;
        ll m2=m1+r>>1;
        if(run(m1)>=run(m2))l=m1;
        else r=m2;
    }

    for(ll i=l;i<=r;++i) if(run(i)<=mid) return 1;
    return 0;
}

void solve()
{
    cin>>n;
    rep(i,1,n) scanf("%lld",&a[i]);
    ll l=1,r=1e15,ans=1;
    while(l<=r){
        ll mid=l+r>>1;
        if(cal(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }
    cout<<ans<<endl;
}
int main()
{
	solve();
}
/*
6
2 2 2 2 2 3
*/

D-牛牛的01限定串

這題我想歪了,難點在於如何計算相似後綴的權值和?我用區間dp去寫了

根據官方題解的方法是,後綴轉換爲前綴,不過轉換爲矩陣方式也是很妙的方法。

dp[i][j]代表t串第i+j個字符時i個0  j 個1的方案數

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1ll<<60;
const int N=1005;
ll dp0[N][N],dp1[N][N],valpre,valsuf,a[N][N];
int n,x,y,cnt0,cnt1;
char s[N],t[N];
bool ck(int x,int y)
{
    if(x>cnt0||x<0||y>cnt1||y<0)return false;
    return true;
}
int main()
{
    scanf("%d %d %d %lld %lld",&n,&cnt0,&cnt1,&valpre,&valsuf);
    scanf("%s %s",s,t);
    for(int i=0;i<=n;++i)
    {
        for(int j=0;j<=n;++j)
        {
            dp0[i][j]=INF;
            dp1[i][j]=-INF;
        }
    }
    x=y=0;

    for(int i=0;i<n;++i)
    {
        if(s[i]=='0')++x;
        else ++y;
        if(ck(x,y))a[x][y]+=valpre;
    }
    x=cnt0,y=cnt1;
    for(int i=n-1;~i;--i)
    {
        if(s[i]=='0')--x;
        else --y;
        if(ck(x,y))a[x][y]+=valsuf;
    }


    dp0[cnt0][cnt1]=dp1[cnt0][cnt1]=a[cnt0][cnt1];


    for(int i=cnt0;~i;--i)//從右下角往左上角走
    {
        for(int j=cnt1;~j;--j)
        {
            if(t[i+j]=='0')
            {
                dp0[i][j]=min(dp0[i][j],dp0[i+1][j]+a[i][j]);
                dp1[i][j]=max(dp1[i][j],dp1[i+1][j]+a[i][j]);
            }
            if(t[i+j]=='1')
            {
                dp0[i][j]=min(dp0[i][j],dp0[i][j+1]+a[i][j]);
                dp1[i][j]=max(dp1[i][j],dp1[i][j+1]+a[i][j]);
            }

            if(t[i+j]=='?')
            {
                dp0[i][j]=min(dp0[i][j],dp0[i+1][j]+a[i][j]);
                dp1[i][j]=max(dp1[i][j],dp1[i+1][j]+a[i][j]);
                dp0[i][j]=min(dp0[i][j],dp0[i][j+1]+a[i][j]);
                dp1[i][j]=max(dp1[i][j],dp1[i][j+1]+a[i][j]);
            }
        }
    }
    printf("%lld %lld\n",dp0[0][0],dp1[0][0]);
    return 0;
}

 

另一種dp:dp[i][j]代表t串第i個字符 有j個0 串 (或j個1串的最大權值和)

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=1005;
typedef long long ll;
int n,c0,c1,mn[N][N],mx[N][N],pre,bf,p0[N],p1[N],b0[N],b1[N];
char s[N],t[N];
int main()
{
    scanf("%d%d%d%d%d",&n,&c0,&c1,&pre,&bf);
    scanf("%s",s+1);
    scanf("%s",t+1);
    for(int i=1;i<=n;i++)
    {
        p0[i]=p0[i-1]+(s[i]=='0');
        p1[i]=p1[i-1]+(s[i]=='1');
    }
    for(int i=n;i>=1;i--)
    {
        b0[i]=b0[i+1]+(s[i]=='0');
        b1[i]=b1[i+1]+(s[i]=='1');
    }
    memset(mn,inf,sizeof(mn));
    memset(mx,-inf,sizeof(mx));


    mn[0][0]=mx[0][0]=0;
    for(int i=0;i<n;i++)
    for(int j=0;j<=min(i,c0);j++)
    {
        if(c0-j==b0[i+1]&&c1-(i-j)==b1[i+1])
        {
            mn[i][j]+=bf;
            mx[i][j]+=bf;
        }
        if(j<c0&&(t[i+1]=='?'||t[i+1]=='0'))
        {
            if(j+1==p0[i+1]&&i-j==p1[i+1])
            {
                mn[i+1][j+1]=min(mn[i+1][j+1],mn[i][j]+pre);
                mx[i+1][j+1]=max(mx[i+1][j+1],mx[i][j]+pre);
            }
            else
            {
                mn[i+1][j+1]=min(mn[i+1][j+1],mn[i][j]);
                mx[i+1][j+1]=max(mx[i+1][j+1],mx[i][j]);
            }
        }
        if(i-j<c1&&(t[i+1]=='?'||t[i+1]=='1'))
        {
            if(j==p0[i+1]&&i-j+1==p1[i+1])
            {
                mn[i+1][j]=min(mn[i+1][j],mn[i][j]+pre);
                mx[i+1][j]=max(mx[i+1][j],mx[i][j]+pre);
            }
            else
            {
                mn[i+1][j]=min(mn[i+1][j],mn[i][j]);
                mx[i+1][j]=max(mx[i+1][j],mx[i][j]);
            }
        }
    }
    printf("%d %d\n",mn[n][c0],mx[n][c0]);
}

F-牛牛的樹形棋

官方的dsu做法太複雜了,代碼複雜,這裏安利一個 樹上主席樹的做法(可能我擅長主席樹)

不過他的nim博弈分析不錯:搬來

子樹的maxdeep就是子樹每個節點的maxdeep值,xor_sum就是每個節點的maxdeep的異或和,max_deep[x]就是x這個子樹的max_deep

這裏瞭解一下nim博弈:

n堆石子,每次從一堆中至少選1個石子,無法行動時則輸了。

結論:n堆石子異或和爲0則先手必敗,否則先手必勝

至於怎麼先手必勝  比如:3堆石子:1 2 4  異或和sum=7  那麼對4這個節點  不考慮4的異或和:7^4=3  那麼我只要將4這堆石子變成3  那麼剩餘的石子的異或和爲0,達到了必敗態。

瞭解了nim博弈後來看這句話:

就懂大概做法了。

怎麼樹上主席樹呢?跑一個dfs序或者時間戳  按照dfs序建主席樹即可。並不是嚴格的樹上主席樹,而是將樹變成一維了。

 

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
typedef long long ll;
int root[N],ls[40*N],rs[40*N],sum[40*N];
int n,m,mx[N],dfn[N],in[N],out[N],cnt,sz;
long long res;
vector<int>G[N];
void dfs(int u,int fa)
{
    dfn[++sz]=u;//dfs序
    in[u]=sz;
    for(int v:G[u]){
        if(v==fa) continue;
        dfs(v,u);
        mx[u]=max(mx[u],mx[v]+1);
    }
    out[u]=sz;
}
void up(int pre,int &o,int l,int r,int pos)
{
    o=++cnt;

    ls[o]=ls[pre];sum[o]=sum[pre]+1;rs[o]=rs[pre];
    if(l==r) return ;
    int mid=l+r>>1;
    if(pos<=mid) up(ls[pre],ls[o],l,mid,pos);
    else up(rs[pre],rs[o],mid+1,r,pos);
}
int qu(int pre,int o,int l,int r,int pos)
{
    if(l==r) return sum[o]-sum[pre];
    int mid=l+r>>1;
    if(pos<=mid) return qu(ls[pre],ls[o],l,mid,pos);
    return qu(rs[pre],rs[o],mid+1,r,pos);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }



    dfs(1,-1);
    int sum=0;
    for(int i=1;i<=n;++i) sum^=mx[i];

    ll ans=0;
    if(!sum)puts("NO");
    else{
        puts("YES");
        for(int i=1;i<=n;i++) up(root[i-1],root[i],0,n,mx[dfn[i]]);


        for(int i=1;i<=n;++i){
            if((sum^mx[i])>=mx[i])continue;
            ans+=qu(root[in[i]-1],root[out[i]],0,n,sum^mx[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章