Hnoi2013题解 bzoj3139~3144
话说好久没写题(解)了。。
先贴份题解:http://wjmzbmr.com/archives/hnoi-2013-%E9%A2%98%E8%A7%A3/(LJ神题解。。Lazycal表示看不懂。。)
以下是Lazycal's题解:
[bzoj3139][Hnoi2013]比赛
对于一个得分序列,可以发现不论如何排列,答案都是一样的。而且n的得分序列可以由n-1的推来。于是,我们可以搜索第一个队伍与其他队伍的比赛结果,由比赛结果得出n-1支队伍的得分序列,递归搜索。然后用每个队伍的得分和队伍个数作为状态,对每个状态进行压缩,利用最小表示法。由于每个队伍最多只能得27分,所以状态总数不超过28^9/(9!)*2(因为只能存在一个0)为千万级别,可以接受。
/**************************************************************
Problem: 3139
User: lazycal
Language: C++
Result: Accepted
Time:732 ms
Memory:1868 kb
****************************************************************/ #include <cstdio>
#include <algorithm>
#include <functional>
#include <map>
const int MOD=1000000000+7;
int n;
std::map<long long,long long>Map;
struct Status
{
int a[11];
long long hash()
{
long long res=0;
for (int i=0;i<=n;++i) res=res*28+a[i];
return res;
}
void sort()
{
std::sort(a+n-a[0]+1,a+1+n/*,std::greater<int>()*/);
}
}start,bound;
long long dfs(const int step,Status now)
{
if (now.a[0] == 1) return Map[now.hash()]=-1;
if (now.a[n-now.a[0]+1] > 3*(n-step+1)) return -1;
if (step > n) {
--now.a[0];
now.sort();
if (Map[now.hash()]) return Map[now.hash()];
return dfs(n-now.a[0]+2,now);
}
long long res=0,tmp;
int idx=n-now.a[0]+1;
if (now.a[idx] >= 3) {
now.a[idx] -= 3;
tmp=dfs(step+1,now);
if (tmp!=-1) (res+=tmp)%=MOD;
now.a[idx] +=3;
}
if (now.a[idx] >= 1 && now.a[step] >= 1) {
now.a[idx] -= 1; now.a[step] -= 1;
tmp=dfs(step+1,now);
if (tmp!=-1) (res+=tmp)%=MOD;
now.a[idx] += 1; now.a[step] += 1;
}
if (now.a[step] >= 3) {
now.a[step] -= 3;
tmp=dfs(step+1,now);
if (tmp!=-1) (res+=tmp)%=MOD;
now.a[step] += 3;
}
res=res?res:-1;
if (step == idx+1) Map[now.hash()]=res;
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("3139.in","r",stdin);
freopen("3139.out","w",stdout);
#endif
scanf("%d",&n);
if (n==1) {puts("1");return 0;}
for (int i=1;i<=n;++i) scanf("%d",start.a+i);
start.a[0]=n;
start.sort();
bound.a[0]=1; bound.a[n]=0; Map[bound.hash()]=1;
printf("%lld\n",dfs(2,start));
}
代码(………………将就着用吧,两种代码高亮的混合使用…………)
[bzoj3140][Hnoi2013]消毒
假设我们选择x*y*z(x<y<z)这样一块区域,则费用为x。也就是说,我们可以视作x次的1*y*z。又因为a*b*c<=5000,所以min{a,b,c}<=17。不妨设min{a,b,c}=a。因此答案<=a。一开始还以为只要把需要消的1*b*c消了就可以了。结果去网上查了题解才知道这样并不一定是最优的……
正解应当是用2^a枚举哪个1*b*c要取,然后转化成二维的最小棋盘覆盖。最小棋盘覆盖就是用最少的行或列把指定点覆盖。没错,这个我也不会……
膜拜了abcdabcd987的题解后才明白。其实是最小点覆盖的模型。只要把行和列拆开,作为二分图的X和Y顶点,然后对于需要覆盖的点从所属的行连向列即可。
/**************************************************************
Problem: 3140
User: lazycal
Language: C++
Result: Accepted
Time:1192 ms
Memory:1320 kb
****************************************************************/ #include <cstdio>
#include <algorithm>
const int N=5000+9;
int ans,a,b,c,T,vis[N],match[N],num,son[N],ec,times;
bool t[N];
struct Edge{int link,next;}es[N*10];
inline void addedge(const int x,const int y)
{
es[++ec].next=son[x];
es[ec].link=y;
son[x]=ec;
}
struct point
{
int x,y,z;
point(const int _x=0,const int _y=0,const int _z=0):x(_x),y(_y),z(_z)
{
if (a<=b && a<=c) std::swap(x,x);
else if (b<=a && b<=c) std::swap(x,y);
else std::swap(x,z);
}
}one[N];
bool find(const int u)
{
for (int i=son[u];i;i=es[i].next) {
const int v=es[i].link;
if (vis[v]==times) continue;
vis[v]=times;
if (!match[v]/*BUGS!!!*/ || find(match[v])) {
match[v]=u;
return 1;
}
}
return 0;
}
void dfs(const int step,int cnt)
{
if (cnt>=ans) return;
if (step>a) {
ec=0;
for (int i=1;i<=b;++i) son[i]=0;
for (int i=0;i<num;++i)
if (!t[one[i].x]) addedge(one[i].y,one[i].z);
for (int i=1;i<=c;++i) match[i]=0;
for (int i=1;i<=b;++i) {
++times;
if ((cnt+=find(i))>=ans) return;
}
ans=cnt;
return;
}
t[step]=1;
dfs(step+1,cnt+1);
t[step]=0;
dfs(step+1,cnt);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("3140.in","r",stdin);
freopen("3140.out","w",stdout);
#endif
scanf("%d",&T);
while (T--) {
scanf("%d%d%d",&a,&b,&c);
num=0;
for (int i=1;i<=a;++i)
for (int j=1;j<=b;++j)
for (int k=1,x;k<=c;++k) {
scanf("%d",&x);
if (x) one[num++]=point(i,j,k);
}
if (a<=b && a<=c) std::swap(a,a);
else if (b<=a && b<=c) std::swap(a,b);
else std::swap(a,c);
ans=a;
dfs(1,0);
printf("%d\n",ans);
}
}
[bzoj3141][Hnoi2013]旅行
这题非常神!的的确确是个好题!
这题我是看着http://tieba.baidu.com/p/2283515454?pid=31800289139&cid=0#318002891399楼的回复以及http://cxjyxx.me/?p=329的题解才懂的……表示自己太弱了……
注:以下a[i],b[i],n,m如题目所述。
首先我们考虑特殊情况,并将b[i]为0的项赋值为-1,令S为所有数之和:
- 全1。此时不难发现答案是|S|/m上取整。
- 全-1。同上,为|S|/m上取整。
- 一半1,一半-1。我们将较少的1or-1与另一边抵消。如 1 1 1 1 1 -1 -1 ------> 1 1 1 (1 1 -1 -1),这样就跟上面的大概一样了。
- 其它情况也差不多。
猜测:ans=|S|/m上取整(S!=0)。
答案:
- ans>=|S|/m上取整,这很显然。
- m<=|S|:不妨设S>0,则将所有的-1根附近的1消掉,最终便只剩1了,ans=|S|/m上取整。
- m>|S|:令s[i]=b[i]+b[i+1]+...+b[n]
- S=0:
- 若s[i]=0的个数>=m,则ans=0,此时取最后一个点和后m-1个s[i]=0的点即可。
- 否则ans=1。因为若ans要为0,则所选的最后一个点必须满足s[i]=0(因为s[i]即为最后的旅途),倒二个也是,依此类推,所选的点s[i]都为0。但s[i]=0的个数<m,所以ans>=1。数据构造:观察s数组,必定是0 1……1 0……0 1……1 0。也就是像一个个小山一样。每次取一条等高线,就可符合题意。
- S!=0:我们首先选出|S|个数,这步同2。剩下的便是一个个与上一个问题一样的子问题了。
- S=0:
字典序:
- S=0 且 m<= s[i]=0的个数:此时只要在s[i]=0的所有i中跑单调队列即可。即维护队列里面a[i]从小到大,然后按照输入顺序入队,只要后面s[i]=0的个数还足够,就一直入队。
- 其它情况:这个比较麻烦。假设我们上一次选了a[last],则下次的i需满足abs(s[last+1]-s[i])<=ans 且 s[i+1]/m'上取整<=ans(m'为剩下的天数) 且 n-i>=m,就相当于一个子问题。于是,可以按s[i]分类,为每个s[i]开一个队列,维护队列a[i]单调递増,只要后面的个数够,就不断入队。选的时候从s[last]-ans到s[last]+ans枚举,先把下标<last的弹出,然后选出a[i]最小的一个。详见代码。
/**************************************************************
Problem: 3141
User: lazycal
Language: C++
Result: Accepted
Time:2696 ms
Memory:41820 kb
****************************************************************/ #include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#define _(x) static_cast<double>(x)
#define __(x) static_cast<int>(x)
const int N=500000+9;
int n,m,sum[N],cnt[N],a[N],tot;
struct node{int l,r,x;}t[N*2];
int newnode(const int l,const int r,const int x)
{
t[++tot].x=x; t[tot].l=l; t[tot].r=r;
return tot;
}
class Queue
{
int be,en,len;
public:
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;
}
bool empty(){return !len;}
int front(){return t[be].x;}
int back(){return t[en].x;}
void pop_back()
{
en=t[en].l;
--len;
}
void pop_front()
{
be=t[be].r;
--len;
}
void push(int x)
{
while (!empty() && a[back()]>a[x]) pop_back();
push_back(x);
}
}Qu[N*2],*qu=Qu+N,Q[N*2],*q=Q+N;
int Min(const int x,const int y)
{
if (a[x]<a[y]) return x;
return y;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("3141.in","r",stdin);
freopen("3141.out","w",stdout);
#endif
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i) {
scanf("%d%d",a+i,sum+i);
if (!sum[i]) sum[i] = -1;
}
for (int i=n-1;i;--i) sum[i] += sum[i+1];
for (int i=n;i;cnt[i] += cnt[i+1], --i)
if (!sum[i]) ++cnt[i];
int S = sum[1], d = S ? (abs(S) - 1)/m + 1 : cnt[1] < m;
cnt[n+1]=-1;
if (!d) {
for (int i=1,j=2; i<m; ++i) {
for (; cnt[j+1]>=m-i; ++j)
if (!sum[j+1]) q[0].push(j);
printf("%d ",a[q[0].front()]);
q[0].pop_front();
}
}else {
a[n+1]=n+1; int la = 0;
for (int i=2; i<=n; ++i)
qu[sum[i]].push_back(i-1);
for (int i=1; i<m; ++i) {
int ans = n+1;
for (int j=sum[la+1]-d; j<=sum[la+1]+d; ++j) {
if (__(ceil(_(abs(j))/(m-i))) > d) continue;
for (; !qu[j].empty() && n-qu[j].front() >= m-i; qu[j].pop_front())
/*printf("test: %d\n",qu[j].front()),*/if (qu[j].front() > la) q[j].push(qu[j].front());
for (; ! q[j].empty() && q[j].front() <= la; q[j].pop_front());
if (!q[j].empty())
ans = Min(ans,q[j].front());
}
la = ans;
printf("%d ",a[ans]);
}
}
printf("%d",a[n]);
}
[bzoj3142][Hnoi2013]数列
这题也不会……实在是太弱了
令a[i]为相邻两天股票价格之差,可以发现对于一个a序列,对ans的贡献是N-a[1]-a[2]-...-a[K-1]。于是答案就是N*M^(K-1)-M*(M+1)/2*M^(K-2)*(K-1)
/**************************************************************
Problem: 3142
User: lazycal
Language: C++
Result: Accepted
Time:0 ms
Memory:804 kb
****************************************************************/ #include <cstdio>
long long P;
long long power(long long x,int k)
{
long long res=1;
for (;k;x=x*x%P,k/=2)
if (k&1) res=res*x%P;
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("3142.in","r",stdin);
freopen("3142.out","w",stdout);
#endif
//N*M^(K-1)-M*(M+1)/2*M^(K-2)*(K-1)
long long N,K,M;
scanf("%lld%lld%lld%lld",&N,&K,&M,&P);
if (K==1) {printf("%lld\n",N%P);return 0;}
long long tmp=power(M,K-2),ans=(N%P*tmp%P*M%P-M*(M+1)/2%P*tmp%P*(K-1)%P)%P;
printf("%lld\n",(ans+P)%P);
}
[bzoj3143][Hnoi2013]游走
这题……不讲了……高斯消元……
/**************************************************************
Problem: 3143
User: lazycal
Language: C++
Result: Accepted
Time:1008 ms
Memory:6880 kb
****************************************************************/ #include <cstdio>
#include <cmath>
#include <algorithm>
const int N=500+9,M=N*N;
const double EPS = 1e-9;
struct edge{int u,v;}es[M];
double times[M],a[N][N];
int d[N],n,m;
void Guass(int equ,int val)
{
for (int col = 1, row = 1; col <= val; ++col, ++row) {
int maxr = row;
for (int i = row + 1; i <= equ; ++i)
if (fabs(a[i][col]) > fabs(a[maxr][col])) maxr = i;
if (maxr != row) for (int i = col; i <= val + 1; ++i) std::swap(a[maxr][i],a[row][i]);
for (int i = col + 1; i <= val + 1; ++i) a[row][i] /= a[row][col];
a[row][col] = 1;
for (int i = 1; i <= equ; ++i)
if (i != row) {
for (int j = col + 1; j <= val + 1; ++j)
a[i][j] -= a[i][col]*a[row][j];
a[i][col]=0;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("3143.in","r",stdin);
freopen("3143.out","w",stdout);
#endif
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i) {
scanf("%d%d",&es[i].u,&es[i].v);
++d[es[i].u]; ++d[es[i].v];
}
for (int i=1;i<=m;++i) {
a[es[i].u][es[i].v]=-static_cast<double>(1)/d[es[i].v];
a[es[i].v][es[i].u]=-static_cast<double>(1)/d[es[i].u];
}
for (int i=1;i<n;++i)
a[i][i]=1,a[i][n]=0;
a[n][n]=0;
a[1][n]=1;
Guass(n-1,n-1);
for (int i=1;i<=m;++i)
times[i]=a[es[i].u][n]/d[es[i].u]+a[es[i].v][n]/d[es[i].v];
std::sort(times+1,times+1+m);
double ans=.0;
for (int i=1;i<=m;++i) ans+=times[i]*(m-i+1);
printf("%.3f\n",ans);
}
[bzoj3144][Hnoi2013]切糕
网络流最小割。增加一层,然后每层向上层连一条边,容量为下层点权。对于第k层的点,每个点都向k-d层连一条容量为无穷大的边。源向底层每个点连容量为无穷大的边,顶层向汇连容量为无穷大的边。然后跑一遍最小割。
/**************************************************************
Problem: 3144
User: lazycal
Language: C++
Result: Accepted
Time:964 ms
Memory:9260 kb
****************************************************************/ #include <cstdio>
#include <cstring>
#include <algorithm>
const int N=65602,INF=0x7fffffff;
int src,sink,Vc,cnt[N],dis[N],ec,p,q,r,d,son[N];
struct Edge
{
int next,v,f;
}es[N*10];
inline int node(const int x,const int y,const int z){return x*p*q+y*q+z;}
inline void addedge(const int x,const int y,const int z)
{
es[ec].v=y; es[ec].f=z;
es[ec].next=son[x]; son[x]=ec++;
}
inline void Addedge(const int x,const int y,const int z){addedge(x,y,z);addedge(y,x,0);}
int sap(const int u,const int aug)
{
if (u==sink) return aug;
int sum=0,mindis=Vc;
for (int i=son[u];i!=-1;i=es[i].next) {
const int v=es[i].v;
if (es[i].f && dis[v]+1==dis[u]) {
int t=sap(v,std::min(aug-sum,es[i].f));
sum+=t; es[i].f-=t; es[i^1].f+=t;
if (sum==aug || dis[src]>=Vc) return sum;
}
if (es[i].f && mindis>dis[v]) mindis=dis[v];
}
if (!sum) {
if (!--cnt[dis[u]]) dis[src]=Vc;
++cnt[dis[u]=mindis+1];
}
return sum;
}
int max_flow()
{
cnt[0]=Vc;
int flow=0;
while (dis[src]<Vc) flow+=sap(src,INF);
return flow;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("3144.in","r",stdin);
freopen("3144.out","w",stdout);
#endif
scanf("%d%d%d%d",&p,&q,&r,&d);
memset(son,-1,sizeof son);
for (int i=0;i<r;++i)
for (int j=0;j<p;++j)
for (int k=0,x;k<q;++k) {
scanf("%d",&x);
Addedge(node(i,j,k),node(i+1,j,k),x);
if (i-d>=0) {
if (j-1>=0) Addedge(node(i,j,k),node(i-d,j-1,k),INF);
if (j+1<p) Addedge(node(i,j,k),node(i-d,j+1,k),INF);
if (k-1>=0) Addedge(node(i,j,k),node(i-d,j,k-1),INF);
if (k+1<q) Addedge(node(i,j,k),node(i-d,j,k+1),INF);
}
}
Vc=(r+1)*p*q+2,src=(r+1)*p*q,sink=(r+1)*p*q+1;
for (int i=0;i<p;++i)
for (int j=0;j<q;++j)
Addedge(src,node(0,i,j),INF),Addedge(node(r,i,j),sink,INF);
printf("%d\n",max_flow());
}
Hnoi2013题解 bzoj3139~3144的更多相关文章
- 【HNOI2013】题解 bzoj3139~bzoj3144
比赛 题目: http://www.lydsy.com/JudgeOnline/problem.php?id=3139 题解: 3$\le$N$\le$10,比较明显是一个搜索题,一开始我是直接搜了 ...
- [HNOI2013]题解
代码在最后 [HNOI2013]比赛 记忆化搜索 把每一位还需要多少分用\(27\)进制压进\(long\) \(long\),\(map\)记忆化一下即可 [HNOI2013]消毒 先考虑在二维平面 ...
- # HNOI2012 ~ HNOI2018 题解
HNOI2012 题解 [HNOI2012]永无乡 Tag:线段树合并.启发式合并 联通块合并问题. 属于\(easy\)题,直接线段树合并 或 启发式合并即可. [HNOI2012]排队 Tag:组 ...
- 【BZOJ3139】[HNOI2013]比赛(搜索)
[BZOJ3139][HNOI2013]比赛(搜索) 题面 BZOJ 洛谷 题解 双倍经验
- BZOJ 3144: [Hnoi2013]切糕
3144: [Hnoi2013]切糕 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1495 Solved: 819[Submit][Status] ...
- bzoj 3144: [Hnoi2013]切糕 最小割
3144: [Hnoi2013]切糕 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 681 Solved: 375[Submit][Status] ...
- ●BOZJ 3144 [Hnoi2013]切糕
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3144 题解: "这是一个经典的最小割模型" ---引用自别人的博客 .. ...
- 【BZOJ 3144】 3144: [Hnoi2013]切糕 (最小割模型)
3144: [Hnoi2013]切糕 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1764 Solved: 965 Description Inp ...
- 【题解】HNOI2013比赛
[题解][P3230 HNOI2013]比赛 将得分的序列化成样例给的那种表格,发现一行和一列是同时确定的.这个表格之前是正方形的,后来长宽都减去一,还是正方形.问题形式是递归的.这就启示我们可以把这 ...
随机推荐
- BZOJ 3270 博物馆 && CodeForces 113D. Museum 期望概率dp 高斯消元
大前提,把两个点的组合看成一种状态 x 两种思路 O(n^7) f[x]表示在某一个点的前提下,这个状态经过那个点的概率,用相邻的点转移状态,高斯一波就好了 O(n^6) 想象成臭气弹,这个和那个的区 ...
- 自定义CheckBox
自定义android的CheckBox按钮图形有两个步骤三种方式: 第一步: 新建Android XML文件,类型选Drawable,根结点选selector,放置在drawable文件夹内,指定各种 ...
- Dilworth定理证明
命题:偏序集能划分成的最少的全序集的个数与最大反链的元素个数相等. (离散数学结构第六版课本P245:把一个偏序集划分成具有全序的子集所需要的最少子集个数与元素在偏序下都是不可比的最大集合的基数之间有 ...
- POJ3159:Candies(差分约束)
Candies Time Limit: 1500MS Memory Limit: 131072K Total Submissions: 39666 Accepted: 11168 题目链接:h ...
- D. Sorting the Coins
Recently, Dima met with Sasha in a philatelic store, and since then they are collecting coins togeth ...
- fscanf函数的应用
转摘自:http://blog.csdn.net/mxgsgtc/article/details/13005675 以前老是被从文本里读取文件,然后逐个的进行字符解析,感觉非常的慢,自从知道了fsca ...
- 【BZOJ4766】文艺计算姬 [暴力]
文艺计算姬 Time Limit: 1 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description "奋战三星期,造台计算机 ...
- bzoj2442&&codevs4654 单调队列优化dp
这道题也是一道单调队列 很明显满足各种性质 f[i]表示i不选前面k-1个都选的最小损失 维护的是个单增队列 q[head]是队列最小值 代码十分简介 注意longlong就okay #include ...
- MyBatis系列四 之 智能标签进行查询语句的拼接
MyBatis系列四 之 智能标签进行查询语句的拼接 使用Foreach进行多条件查询 1.1 foreach使用数组进行多条件查询 在MyBatis的映射文件中进行如下配置 <!--根据数组进 ...
- A way escape rbash
hacker@beta:~$ ls -rbash: /usr/bin/python: restricted: cannot specify `/' in command names ryuu@beta ...