【题解】HNOI-2013 Day1解题报告
2018-03-18 09:10
399 查看
题目链接(洛谷)
T1 比赛
T2 消毒
T3 旅行
代码在本文末尾
算法:记忆化搜索+状态压缩
突破口:状压记忆化优化搜索
正确思路:发现n≤10n≤10,复杂度玄学,锁定搜索
一开始看到这题觉得比较像高斯消元之类的 可能被Day2洗脑了吧 但发现复杂度如果是O(n3)O(n3),那数据就太松了
看到这题的n≤10n≤10就会往搜索的方面想,但发现普通的搜索会T,加上优化
发现现有的优化中(蒟蒻总共就只会那么几种优化)记忆化在这题中优化效果比较明显,那就用上记忆化,同时数组下表用一个29进制数表示,用map保存即可
算法:dfs+二分图匹配
突破口:从二维平面入手
正确思路:考虑两维的情况,发现第三维可以枚举
这题是在三维长方体解决问题,根据以往的经验,省选题从简单情况入手深入
先考虑二维的情况:给定一个n∗mn∗m的矩形,给定一些要处理的点,每次可以选定一块a∗ba∗b的矩形并处理内部节点,费用为min(a,b)min(a,b),问最少花费多少可以将所有给定点处理
这个子问题是二分图匹配中经典的最小点覆盖模型
解释一下:首先确定一点,每次只取一长条(1∗a1∗a)可以达到最优,因为如果是取一个a∗b(a<b)a∗b(a<b)的矩形,等价于处理aa条(1∗b)(1∗b)的矩形
然后问题等价于每次可以选取一列或一行,若(x,y)(x,y)处有需处理的点,则将xx向yy连线,最后求最小点覆盖即可,至此二维下的问题得以解决
但这种方法我不知道怎么放到直接放到三维中去,因为蒟蒻并不会三分图下的最小点覆盖
瞟一眼数据,发现数据a·b·c≤5000a·b·c≤5000,这意味着min(a,b,c)≤5000‾‾‾‾‾√3≈17min(a,b,c)≤50003≈17,则第三维可以使用枚举的方法,枚举这一层是使用1的代价直接削掉还是与其它层一起处理,对于与其它层一起处理可以将这些层的点缩到一个平面内(因为可以设置每次处理的宽为1,高为长方体的高)
算法:推导结论+优先队列模拟
突破口:从特殊情况下入手
正确思路:考虑答案为零的情况,进而猜出结论
这题真心不会,以为是一个Dp加上一个什么玄学优化之类的,看了几篇题解问了dalao才勉强弄明白的
简述一下dalao的解法:
将数列转化成仅包含±1±1的数列,求后缀和(因为这样可以快速判断一段序列中的零一个数差)
将后缀和画成一条函数图像,感受一下:
考虑序列和为零的情况,发现如果旅途中有至少mm个点使得切分出来的数列为零则答案为零(显然),否则为一(因为可以在函数图像中上下距离为的一条水平线与函数的交界处取得,以将答案限制在1
考虑序列和为rr的情况,找到第一个后缀和等于零的位置,这个位置后面的部分就是上面的情况,最大值为1或0,不影响前面的答案,接下来只取第一个元素到第一个后缀和为零的位置,很容易发现答案就是⌈rm⌉⌈rm⌉
至于字典序,则使用优先队列模拟求解
消毒
旅行
T1 比赛
T2 消毒
T3 旅行
代码在本文末尾
比赛
主要考察:代码实现算法:记忆化搜索+状态压缩
突破口:状压记忆化优化搜索
正确思路:发现n≤10n≤10,复杂度玄学,锁定搜索
一开始看到这题觉得比较像高斯消元之类的 可能被Day2洗脑了吧 但发现复杂度如果是O(n3)O(n3),那数据就太松了
看到这题的n≤10n≤10就会往搜索的方面想,但发现普通的搜索会T,加上优化
发现现有的优化中(蒟蒻总共就只会那么几种优化)记忆化在这题中优化效果比较明显,那就用上记忆化,同时数组下表用一个29进制数表示,用map保存即可
消毒
主要考察:思维灵活性算法:dfs+二分图匹配
突破口:从二维平面入手
正确思路:考虑两维的情况,发现第三维可以枚举
这题是在三维长方体解决问题,根据以往的经验,省选题从简单情况入手深入
先考虑二维的情况:给定一个n∗mn∗m的矩形,给定一些要处理的点,每次可以选定一块a∗ba∗b的矩形并处理内部节点,费用为min(a,b)min(a,b),问最少花费多少可以将所有给定点处理
这个子问题是二分图匹配中经典的最小点覆盖模型
解释一下:首先确定一点,每次只取一长条(1∗a1∗a)可以达到最优,因为如果是取一个a∗b(a<b)a∗b(a<b)的矩形,等价于处理aa条(1∗b)(1∗b)的矩形
然后问题等价于每次可以选取一列或一行,若(x,y)(x,y)处有需处理的点,则将xx向yy连线,最后求最小点覆盖即可,至此二维下的问题得以解决
但这种方法我不知道怎么放到直接放到三维中去,因为蒟蒻并不会三分图下的最小点覆盖
瞟一眼数据,发现数据a·b·c≤5000a·b·c≤5000,这意味着min(a,b,c)≤5000‾‾‾‾‾√3≈17min(a,b,c)≤50003≈17,则第三维可以使用枚举的方法,枚举这一层是使用1的代价直接削掉还是与其它层一起处理,对于与其它层一起处理可以将这些层的点缩到一个平面内(因为可以设置每次处理的宽为1,高为长方体的高)
旅行
主要考察:思维能力算法:推导结论+优先队列模拟
突破口:从特殊情况下入手
正确思路:考虑答案为零的情况,进而猜出结论
这题真心不会,以为是一个Dp加上一个什么玄学优化之类的,看了几篇题解问了dalao才勉强弄明白的
简述一下dalao的解法:
将数列转化成仅包含±1±1的数列,求后缀和(因为这样可以快速判断一段序列中的零一个数差)
将后缀和画成一条函数图像,感受一下:
考虑序列和为零的情况,发现如果旅途中有至少mm个点使得切分出来的数列为零则答案为零(显然),否则为一(因为可以在函数图像中上下距离为的一条水平线与函数的交界处取得,以将答案限制在1
考虑序列和为rr的情况,找到第一个后缀和等于零的位置,这个位置后面的部分就是上面的情况,最大值为1或0,不影响前面的答案,接下来只取第一个元素到第一个后缀和为零的位置,很容易发现答案就是⌈rm⌉⌈rm⌉
至于字典序,则使用优先队列模拟求解
代码部分
比赛#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 ,b ; ll n,m,ans,top(0); map <ll,ll> f ; 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 ==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 ; int head ,pos[4] ,fr ,bo ,Floor ; 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 ,cnt ,a ,__(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 ); }
相关文章推荐
- 【题解】HNOI-2013 Day2解题报告
- NOIP2013提高组Day1 解题报告
- 【NOIP2013】【提高组】【Day1】【解题报告】
- 【题解】HNOI-2014 Day2解题报告
- 2016.7.12 NOIP2013提高组day1解题报告(未完成版)
- luogu解题报告:HNOI2012永无乡
- 2016.8.25 NOIP2012 day1 解题报告
- Luogu1967 [NOIP2013] 货车运输 解题报告【Kruskal】【LCA】【倍增】
- NOIP2013Day2T1积木大赛解题报告
- POJ 2013 解题报告
- noip2013提高组 积木大赛解题报告
- 2013 Asia Regional Changchun 解题报告
- 2013 Multi-University Training Contest 4 解题报告(更新中)
- 2016.9.3测试解题报告(NOIP2014 day1 day2)
- 2013 Multi-University Training Contest 7 解题报告(更新中)
- NOIP2013 花匠解题报告
- ZOJ Monthly, March 2013 解题报告
- Croc Champ 2013 - Round 2 (Div. 2 Edition) E. Cube Problem 解题报告
- [NOIP2013]车站分级 解题报告
- HDU杭电2013 多校第一场解题报告