【题解】HNOI-2013 Day1解题报告

题目链接(洛谷)

T1 比赛
T2 消毒
T3 旅行

代码在本文末尾

比赛

  • 主要考察:代码实现
  • 算法:记忆化搜索+状态压缩
  • 突破口:状压记忆化优化搜索
  • 正确思路:发现 n10 n ≤ 10 ,复杂度玄学,锁定搜索

一开始看到这题觉得比较像高斯消元之类的 可能被Day2洗脑了吧 但发现复杂度如果是 O(n3) O ( n 3 ) ,那数据就太松了

看到这题的 n10 n ≤ 10 就会往搜索的方面想,但发现普通的搜索会T,加上优化

发现现有的优化中(蒟蒻总共就只会那么几种优化)记忆化在这题中优化效果比较明显,那就用上记忆化,同时数组下表用一个29进制数表示,用map保存即可

消毒

  • 主要考察:思维灵活性
  • 算法:dfs+二分图匹配
  • 突破口:从二维平面入手
  • 正确思路:考虑两维的情况,发现第三维可以枚举

这题是在三维长方体解决问题,根据以往的经验,省选题从简单情况入手深入

先考虑二维的情况:给定一个 nm n ∗ m 的矩形,给定一些要处理的点,每次可以选定一块 ab a ∗ b 的矩形并处理内部节点,费用为 min(a,b) m i n ( a , b ) ,问最少花费多少可以将所有给定点处理

这个子问题是二分图匹配中经典的最小点覆盖模型

解释一下:首先确定一点,每次只取一长条( 1a 1 ∗ a )可以达到最优,因为如果是取一个 ab(a<b) a ∗ b ( a < b ) 的矩形,等价于处理 a a (1b)的矩形

然后问题等价于每次可以选取一列或一行,若 (x,y) ( x , y ) 处有需处理的点,则将 x x y连线,最后求最小点覆盖即可,至此二维下的问题得以解决

但这种方法我不知道怎么放到直接放到三维中去,因为蒟蒻并不会三分图下的最小点覆盖

瞟一眼数据,发现数据 a·b·c5000 a · b · c ≤ 5000 ,这意味着 min(a,b,c)5000317 m i n ( a , b , c ) ≤ 5000 3 ≈ 17 ,则第三维可以使用枚举的方法,枚举这一层是使用1的代价直接削掉还是与其它层一起处理,对于与其它层一起处理可以将这些层的点缩到一个平面内(因为可以设置每次处理的宽为1,高为长方体的高)

旅行

  • 主要考察:思维能力
  • 算法:推导结论+优先队列模拟
  • 突破口:从特殊情况下入手
  • 正确思路:考虑答案为零的情况,进而猜出结论

这题真心不会,以为是一个Dp加上一个什么玄学优化之类的,看了几篇题解问了dalao才勉强弄明白的

简述一下dalao的解法:

将数列转化成仅包含 ±1 ± 1 的数列,求后缀和(因为这样可以快速判断一段序列中的零一个数差)

将后缀和画成一条函数图像,感受一下:

考虑序列和为零的情况,发现如果旅途中有至少 m m 个点使得切分出来的数列为零则答案为零(显然),否则为一(因为可以在函数图像中上下距离为的一条水平线与函数的交界处取得,以将答案限制在1

考虑序列和为r的情况,找到第一个后缀和等于零的位置,这个位置后面的部分就是上面的情况,最大值为1或0,不影响前面的答案,接下来只取第一个元素到第一个后缀和为零的位置,很容易发现答案就是 rm ⌈ r m ⌉

至于字典序,则使用优先队列模拟求解

代码部分

比赛

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rg register
#define cl(x) memset(x,0,sizeof(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)>0?(x):(-(x)))

template <typename _Tp> inline _Tp read(_Tp&x){
    rg char c11=getchar(),ob=0;x=0;
    while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=15;const ll p=1000000007;
ll a[N],b[N];
ll n,m,ans,top(0);

map <ll,ll> f[N];

ll calc(ll,ll,ll);

ll dfs(ll);

int main(){
    read(n);
    for(rg int i=1;i<=n;++i)read(a[i]);
    sort(a+1,a+n+1);
    printf("%lld\n",dfs(1));
    return 0;
}

ll calc(ll x,ll y,ll now){
    if(x>n)return y?0:dfs(now+1);
    if(3*(n-x+1)<y)return 0;
    ll cnt=0;
    if(y>=3)cnt=(cnt+calc(x+1,y-3,now))%p;
    if(y&&a[x]){
        --a[x];
        cnt=(cnt+calc(x+1,y-1,now))%p;
        ++a[x];
    }
    if(a[x]>=3){
        a[x]-=3;
        cnt=(cnt+calc(x+1,y,now))%p;
        a[x]+=3;
    }
    return cnt;
}

ll dfs(ll x){
    if(x==n)return a[n]==0;
    top=0;
    for(rg int i=x;i<=n;++i)b[++top]=a[i];
    sort(b+1,b+top+1);
    ll tmp=0;
    for(rg int i=1;i<=top;++i)tmp=tmp*29+b[i];
    if(f[x].find(tmp)!=f[x].end())return f[x][tmp];
    else return f[x][tmp]=calc(x+1,a[x],x);
}

消毒

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
typedef long long ll;
#define rg register
#define min(x,y) ((x)<(y)?(x):(y))

template <typename _Tp> inline _Tp read(_Tp&x){
    rg char c11=getchar(),ob=0;x=0;
    while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=505000;
struct Edge{int v,nxt;}e[N];
int head[N],pos[4][N],fr[N],bo[N],Floor[N];
int a,b,c,mi,Ans,m_,_;

inline void add(int u,int v){e[++_].v=v,e[_].nxt=head[u],head[u]=_;}

inline char find(int x){
    for(rg int i=head[x];i;i=e[i].nxt)
        if(!bo[e[i].v]){
            bo[e[i].v]=1;
            if(!fr[e[i].v]||find(fr[e[i].v])){
                fr[e[i].v]=x;
                return 1;
            }
        }
    return 0;
}

void work(int x){
    for(rg int i=1;i<=b;++i)head[i]=0;
    for(rg int i=1;i<=c;++i)fr[i]=0;
    _=0;
    int ans=0;
    for(rg int i=0;i<a;++i)
        if(x&(1<<i))Floor[i+1]=0,++ans;
        else Floor[i+1]=1;
    for(rg int i=1;i<=m_;++i)
        if(Floor[pos[1][i]])
            add(pos[2][i],pos[3][i]);
    for(rg int i=1;i<=b;++i){
        for(rg int j=1;j<=c;++j)bo[j]=0;
        ans+=find(i);
    }
    Ans=min(Ans,ans);
    return ;
}

int main(){
    int T,x;read(T);
    while(T--){
        m_=0,Ans=0x7fffffff;
        read(a),read(b),read(c);
        mi=min(a,min(b,c));
        for(rg int i=1;i<=a;++i)
        for(rg int j=1;j<=b;++j)
        for(rg int k=1;k<=c;++k)
            if(read(x))
                pos[1][++m_]=i,pos[2][m_]=j,pos[3][m_]=k;
        if(mi==b)swap(a,b),swap(pos[1],pos[2]);
        if(mi==c)swap(a,c),swap(pos[1],pos[3]);
        for(rg int i=0;i<(1<<a);++i)
            work(i);
        printf("%d\n",Ans);
    }
    return 0;
}

旅行

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rg register
#define cl(x) memset(x,0,sizeof(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)>0?(x):(-(x)))

#define Min(x,y) ((a[(x)])<(a[(y)])?(x):(y))

template <typename _Tp> inline _Tp read(_Tp&x){
    rg char c11=getchar(),ob=0;x=0;
    while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=505000;
int n,m,opt[N],cnt[N],a[N],__(0);

struct node{int l,r,x;}t[N<<1];

struct Queue{
    int be,en,len;
    inline int newnode(int l,int r,int x){t[++__].x=x,t[__].l=l,t[__].r=r;return __;}
    inline void push_back(int x){
        if(!len)be=en=newnode(0,0,x);
        else t[en].r=newnode(en,0,x),en=t[en].r;
        ++len;
    }
    inline bool empty(){return !len;}
    inline int front(){return t[be].x;}
    inline int back(){return t[en].x;}
    inline void pop_back(){en=t[en].l;--len;}
    inline void pop_front(){be=t[be].r;--len;}
    inline void push(int x){
        while(!empty()&&a[back()]>a[x])pop_back();
        push_back(x);
    }
}Qu[N<<1],*qu=Qu+N,Q[N<<1],*q=Q+N;

int main(){
    read(n);read(m);
    for(rg int i=1;i<=n;++i)
        read(a[i]),opt[i]=read(opt[i])?1:-1;
    for(rg int i=n-1;i;--i)opt[i]+=opt[i+1];
    for(rg int i=n;i;--i)
        cnt[i]=cnt[i+1]+(!opt[i]);
    int r;
    if(opt[1]) r=(abs(opt[1])-1)/m+1;
    else r=cnt[1]<m;
    cnt[n+1]=-1;
    if(!r)
        for(rg int i=1,j=2;i<m;++i){
            for(;cnt[j+1]>=m-i;++j)
                if(!opt[j+1])q[0].push(j);
            printf("%d ",a[q[0].front()]);
            q[0].pop_front();
        }
    else {
        a[n+1]=n+1;int las(0);
        for(rg int i=2;i<=n;++i)
            qu[opt[i]].push_back(i-1);
        for(rg int i=1;i<m;++i){
            int ans=n+1;
            for(rg int j=opt[las+1]-r;j<=opt[las+1]+r;++j){
                if((int)(ceil((double)(abs(j))/(m-i)))>r)continue;
                for(;!qu[j].empty()&&n-qu[j].front()>=m-i;qu[j].pop_front())
                    if(qu[j].front()>las)q[j].push(qu[j].front());
                for(;!q[j].empty()&&q[j].front()<=las;q[j].pop_front());
                if(!q[j].empty())
                    ans=Min(ans,q[j].front());
            }
            las=ans;
            printf("%d ",a[ans]);
        }
    }
    printf("%d\n",a[n]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值