HNOI2018题解(转盘暂缺)

道路

简单的难以置信的树上dp,一度以为自己看错题了

#include<bits/stdc++.h>
#define N 20010
using namespace std;
int a[N],b[N],c[N];
int gl[N],tl[N];
long long dp[N][42][42];
int n,tot;
long long dfs(int p,int x,int y){
    if(p<0){
        p=-p;
        return 1ll*c[p]*(a[p]+x)*(b[p]+y);
    }
    if(dp[p][x][y]!=0) return dp[p][x][y];
    return dp[p][x][y]=min(dfs(gl[p],x+1,y)+dfs(tl[p],x,y),dfs(gl[p],x,y)+dfs(tl[p],x,y+1));
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++) scanf("%d%d",&gl[i],&tl[i]);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
    printf("%lld\n",dfs(1,0,0));
    return 0;
}

排列

神奇贪心,不难发现限制条件可以转化为在按顺序有根森林中选点,每次只能选某个根节点,然后把这个点删掉。

这样还不够,我们考虑每次选择:如果最小的权值是个根节点,肯定直接取走,否则在取走这个点的父亲节点后,下一次一定直接选它,这样的话我们可以每次将最小节点与它的父亲并到一起,新的权值为这个块里的权值平均值(显然的贪心???)。这里可以用并查集和堆维护一下。

#include<bits/stdc++.h>
#define N 500100
using namespace std;
inline int read(){
    int a=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch<='9'&&ch>='0') a=a*10+ch-'0',ch=getchar();
    return a;
}
struct Node{
    long long zon;
    int siz,zg;
};
bool operator <(Node a,Node b){return a.zon*b.siz>b.zon*a.siz;}
priority_queue<Node> q;
int n,m;
int fa[N];
bool done[N];
long long w[N];
int a[N],s[N];
int find_fa(int a){
    if(fa[a]==a) return a;
    return fa[a]=find_fa(fa[a]);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=n;i++) s[i]=1,q.push((Node){w[i],1,i});
    long long ans=0;
    int tot=0;
    while(!q.empty()){
        Node st=q.top();q.pop();
        if(done[st.zg]) continue;
        if(s[st.zg]==st.siz) done[st.zg]=1;
        else continue;
        if(find_fa(a[st.zg])==0){
            ans+=st.zon*(tot+1);
            tot+=st.siz;
            fa[st.zg]=0;
        }
        else{
            int l=find_fa(a[st.zg]),r=st.zg;
            fa[r]=l;
            ans+=w[r]*s[l];
            s[l]+=s[r];
            w[l]+=w[r];
            q.push((Node){w[l],s[l],l});
        }
    }
    if(tot!=n) puts("-1");
    else printf("%lld\n",ans);
    return 0;
}

游戏

以下是最慢点3s,BZOJ AC现场90分的N^2暴力

#include<bits/stdc++.h>
#define N 1001000
using namespace std;
inline void read(int &a){
    a=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch<='9'&&ch>='0') a=a*10+ch-'0',ch=getchar();
}
int n,m,q;
int ned[N];
int l[N],r[N];
int main(){
    read(n);read(m);read(q);
    ned[n]=-1,ned[0]=-1;
    for(int i=0,a,b;i<m;i++){
        read(a);read(b);
        ned[a]=b;
    }
    for(int i=n;i>=1;i--){
        l[i]=i,r[i]=i;
        int f=1;
        while(f){
            f=0;
            if(ned[l[i]-1]<=r[i]&&ned[l[i]-1]>=l[i]||ned[l[i]-1]==0){
                l[i]--,f=1;
            }
            if(ned[r[i]]<=r[i]&&ned[r[i]]>=l[i]||ned[r[i]]==0){
                r[i]++,f=1;
                l[i]=min(l[i],l[r[i]]);
                r[i]=max(r[i],r[r[i]]);
            }
        }
    }
    for(int i=1,s,t;i<=q;i++){
        scanf("%d%d",&s,&t);
        if(t<=r[s]&&t>=l[s]) puts("YES");
        else puts("NO");
    }
    return 0;
}

寻宝游戏

本机卡常AC,提交疯狂TLE,复杂度O(NMQ)。。。

#include<bits/stdc++.h>
#define M 5010
#define N 1024
using namespace std;
char buf[30000000],*cp=buf;
inline void read(int &a){
    a=0;
    while(*cp<'0'||*cp>'9') *cp++;
    while(*cp<='9'&&*cp>='0') a=a*10+(*cp++ & 15);
}
inline void rd(char *s){
    while(!isdigit(*cp)) cp++;
    while(isdigit(*cp)) *s++ = *cp++;
    *s=0;
}
const int MOD=1e9+7;
int n,m,q,ans;
char s[M];
int a[N][M],c[M];
int cop[N][M];
int cf[N];
void dfs(int p,int res){
    if(!res){
        ans=ans+cf[p];
        if(ans>=MOD) ans-=MOD;
        return;
    }
    if(p==0){
        for(register int i=0;i!=res;++i) if(c[cop[p][i]]) return;
        ans++;
        if(ans>=MOD) ans-=MOD;
        return;
    }
    int fa=1,fo=1;
    for(register int i=0;i!=(res/4)*4;i+=4){
        if(a[p][cop[p][i]]==0&&c[cop[p][i]]){
            fa=0;
            break;
        }
        if(a[p][cop[p][i+1]]==0&&c[cop[p][i+1]]){
            fa=0;
            break;
        }
        if(a[p][cop[p][i+2]]==0&&c[cop[p][i+2]]){
            fa=0;
            break;
        }
        if(a[p][cop[p][i+3]]==0&&c[cop[p][i+3]]){
            fa=0;
            break;
        }
    }
    for(register int i=(res/4)*4;i!=res;i++)
        if(a[p][cop[p][i]]==0&&c[cop[p][i]]){
            fa=0;
            break;
        }
        for(register int i=0;i!=(res/4)*4;i+=4){
        if(a[p][cop[p][i]]&&c[cop[p][i]]==0){
            fo=0;
            break;
        }
        if(a[p][cop[p][i+1]]&&c[cop[p][i+1]]==0){
            fo=0;
            break;
        }
        if(a[p][cop[p][i+2]]&&c[cop[p][i+2]]==0){
            fo=0;
            break;
        }
        if(a[p][cop[p][i+3]]&&c[cop[p][i+3]]==0){
            fo=0;
            break;
        }
    }
    for(register int i=(res/4)*4;i!=res;i++)
        if(a[p][cop[p][i]]&&c[cop[p][i]]==0){
            fo=0;
            break;
        }
    int nres;
    if(fa){
        nres=0;
        for(register int i=0;i!=(res/4)*4;i+=4){
            if(a[p][cop[p][i]]) cop[p-1][nres++]=cop[p][i];
            if(a[p][cop[p][i+1]]) cop[p-1][nres++]=cop[p][i+1];
            if(a[p][cop[p][i+2]]) cop[p-1][nres++]=cop[p][i+2];
            if(a[p][cop[p][i+3]]) cop[p-1][nres++]=cop[p][i+3];
        }
        for(register int i=(res/4)*4;i<res;i++){
            if(a[p][cop[p][i]]) cop[p-1][nres++]=cop[p][i];
        }
        dfs(p-1,nres);
    }
    if(fo){
        nres=0;
        for(register int i=0;i!=(res/4)*4;i+=4){
            if(!a[p][cop[p][i]]) cop[p-1][nres++]=cop[p][i];
            if(!a[p][cop[p][i+1]]) cop[p-1][nres++]=cop[p][i+1];
            if(!a[p][cop[p][i+2]]) cop[p-1][nres++]=cop[p][i+2];
            if(!a[p][cop[p][i+3]]) cop[p-1][nres++]=cop[p][i+3];
        }
        for(register int i=(res/4)*4;i<res;i++){
            if(!a[p][cop[p][i]]) cop[p-1][nres++]=cop[p][i];
        }
        dfs(p-1,nres);
    }
}
int main(){
    buf[fread(buf,1,30000000,stdin)] = 0;
    read(n);read(m);read(q);
    cf[0]=1;
    for(int i=1;i<=n+1;i++) cf[i]=cf[i-1]*2%MOD;
    for(int i=1;i<=n;i++){
        rd(s);
        for(int j=0;j<m;j++) a[i][j]=s[j]-'0';
    }
    while(q--){
        ans=0;
        rd(s);
        for(int i=0;i<m;i++) c[i]=s[i]-'0';
        for(int i=0;i<m;i++) cop[n][i]=i;
        dfs(n,m);
        printf("%d\n",ans);
    }
    return 0;
}

毒瘤

考虑建出一个生成树,那么会余下一些非树边(返祖边),最多11条。

考虑枚举这些边的选择情况,一共有三种:选一端,选另一端,都不选。这样就可以DP了,复杂度n*3^11,期望得分60-70。

事实上,我们只用考虑两种情况:选深度小的一端,不选深度小的一端,在DP的把深度大的一端也DP进去就行了,复杂度n*2^11,期望得分70-80分。

这还过不去。。。发现每个节点并不可能包含所有的端点,也就是说这东西其实跑不满,或许可以弄个动态的数组乱搞一下?但是我实在不想写了。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值