Problem A 蛋糕

将$n \times m $大小的蛋糕切成每块为$1 \times 1$大小的$n\times m$块。

交换任意两块蛋糕的切割顺序的方案算作一种。

对于$100 \%$的数据满足$1 \leq n,m \leq 300$

Solution : 一个比较明显的DP

设$f[i][j]$表示蛋糕大小为$i \times j$时候的答案。

  当前步可以在第$k(1\leq k \leq i-1)$行切一刀分成$[1,k]$和$[k+1,i]$两部分;

  或者可以在第$k(q\leq k \leq j-1)$列切一刀分成$[1,k]$和$[k+1,j]$ 两部分。

  问题就可以转化为两个子问题了(由乘法原理可以合并),然后把所有子问题相加就是最后的答案。

 枚举状态是$O(n^2)$,然后枚举转移是$O(n)$的复杂度,总复杂度是$O(n^3)$

# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=,mo=1e9+;
int f[N][N];
int dfs(int i,int j)
{
if (i== && j==) return ;
if (i< || j<) return ;
if (f[i][j]!=-) return f[i][j];
int ret=;
for (int k=;k<=i-;k++)
ret=(ret+dfs(k,j)*dfs(i-k,j)%mo)%mo;
for (int k=;k<=j-;k++)
ret=(ret+dfs(i,k)*dfs(i,j-k)%mo)%mo;
return f[i][j]=ret;
}
signed main()
{
int n,m; scanf("%lld%lld",&n,&m);
memset(f,-,sizeof(f));
printf("%lld\n",dfs(n,m));
return ;
}

A.cpp

Problem B 找钱 

有$n$种面值为$a_i$的纸币,小L手上有$b_i$张,而商店手上有$c_i$张。

小L到商店去购买价格为$X$的商品,问有多少种不同的付钱-找钱的方法。

一种不同的且合法的方案满足,小$L$付出的纸币必须是必要的且付钱方案或者找钱的方案有不同。

对于$100\%$的数据满足$n \leq 10^3 ,\leq a_i,b_i,c_i \leq 10^4$

Solution : 还是一个比较明显的(背包)DP

  设$f[i][j]$表示小L在后$n$种纸币(纸币币值需要单调)中找出钱数位$j$的方案数。

  设$g[i][j]$表示商店在前$i$种纸币中找出钱数为$j$的方案数。

  可以有一个显然的多重背包的转移,

  $g[i][j]=g[i][j]+g[i-1][j-k*a[i]] , f[i][j]=f[i][j]+f[i+1][j-k*a[i]]$

  初始值为$f[n+1][0] = 1, g[0][0] = 1$ ,复杂度大概是$O(n X \sum c[i])$

  上面的DP可以用前缀和优化,上一层的转移区间每一次会向右平移$a[i]$个单位,于是我们只需要计算上一个区间里转移区间内的和即可。

  所以我们只需要枚举余数$j$和跳$a[i]$单位的步数$k$即可用$j+k\times a[i]$拼成一个物品了。

  具体来说,若把上一层转移而来的元素记为$j$那么,当前元素就是$j + k\times a[i]$,当前元素的转移区间就是$[j,j+k\times a[i]]$

  所以,每当$k$+1,转移区间就向右平移$a[i]$个单位,于是我们只需要维护一个变量$Sum$记录一下$i-1$层的转移区间的和即可。每一次滑动$O(1)$计算。

  用上述方法,我们可以将复杂度优化到$O(n Max\{a_i , X\})$ 。

# include <bits/stdc++.h>
# define ll long long
using namespace std;
const int mo=1e9+;
int a[],b[],c[];
int f[][],n,m,g[][];
signed main()
{
// freopen("deal12.in","r",stdin);
// freopen("deal13.out","w",stdout);
scanf("%d%d",&n,&m);
int Lim=m;
for (int i=;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]),Lim=max(Lim,a[i]);
g[][]=;
// for (int i=1;i<=n;i++) {
// for (int j=0;j<=m;j++) {
// for (int k=0;k<=min(j/a[i],c[i]);k++)
// g[i][j]=(g[i][j]+g[i-1][j-k*a[i]])%mo;
// }
// }
for (int i=;i<=n;i++)
for (int j=;j<=a[i]-;j++) {
int sum=;
for (int k=;j+k*a[i]<=Lim;k++) {
sum=(sum+g[i-][j+k*a[i]])%mo;
if (k>c[i]) sum=((sum-g[i-][j+a[i]*(k-c[i]-)])%mo+mo)%mo;
g[i][j+a[i]*k]=sum;
}
} f[n+][]=;
// for (int i=n;i>=1;i--) {
// for (int j=0;j<=2*m;j++) {
// for (int k=0;k<=min(j/a[i],b[i]);k++)
// f[i][j]=(f[i][j]+f[i+1][j-k*a[i]])%mo;
// }
// }
for (int i=n;i>=;i--)
for (int j=;j<=a[i]-;j++) {
int sum=;
for (int k=;j+k*a[i]<=*Lim;k++) {
sum=(sum+f[i+][j+k*a[i]])%mo;
if (k>b[i]) sum=((sum-f[i+][j+a[i]*(k-b[i]-)])%mo+mo)%mo;
f[i][j+a[i]*k]=sum;
}
}
ll ans=;
for (int i=;i<=Lim;i++) {
int w=;
for (int j=;j<=n;j++) if (a[j]>i) { w=j; break;}
if (w) ans=(ans+(ll)g[n][i]*f[w][m+i]%mo)%mo;
}
printf("%lld\n",ans);
return ;
}

B.cpp

Problem C 城镇

初始离散的$n$个点,每次添加$1$条边,共添加$n-1$次构成一棵树。

每一次连边之后,输出在该边所在的连通块中的直径为多少。

对于$100 \%$的数据 $n \leq 3\times 10^5$

 Solution :

  首先想到每一次合并直径,若一个连通块中直径为$s1,t1$,另外一个连通块中直径为$s2,t2$

  那么合并后的连通块直径为$s1,t1,s2,t2$四个点中取2个点组成路径的最长路。

  如果不是这样,那么必然存在一条更加长的直径,与直径的最长性矛盾,所以定理显然成立。

  于是我们就可以用并查集维护连通块。

  我们发现每次合并时将size较小的块合并到size较大的块上即可。

  这样子做的复杂度是$O(n \ log_2 \ n)$

  由于还有求$LCA$所以本题的时间复杂度是$O(n \ {log_2}^2 \ n)$

# include<bits/stdc++.h>
using namespace std;
const int N=3e5+;
struct rec{
int pre,to;
}a[N<<];
struct A{
int size,s,t;
}mem[N];
int head[N],tot,gi[N],rec[N],dep[N],g[N][];
int n;
void adde(int u,int v)
{
a[++tot].pre=head[u];
a[tot].to=v;
head[u]=tot;
}
int father(int x)
{
if (gi[x]==x) return x;
return gi[x]=father(gi[x]);
}
void dfs(int u,int fa)
{
rec[++rec[]]=u; g[u][]=fa; dep[u]=dep[fa]+;
for (int i=head[u];i;i=a[i].pre) {
int v=a[i].to; if (v==fa) continue;
dfs(v,u);
}
}
int lca(int u,int v) {
if (dep[u]<dep[v]) swap(u,v);
for (int i=;i>=;i--)
if (dep[g[u][i]]>=dep[v]) u=g[u][i];
if (u==v) return u;
for (int i=;i>=;i--)
if (g[u][i]!=g[v][i]) u=g[u][i],v=g[v][i];
return g[u][];
}
int getdist(int u,int v) {
int l=lca(u,v);
return dep[u]+dep[v]-*dep[l];
}
int main()
{
scanf("%d",&n);
for (int i=;i<=n;i++) gi[i]=i,mem[i].size=,mem[i].s=mem[i].t=i;
int m=n-;
while (m--) {
int u,v; scanf("%d%d",&u,&v);
int fx=father(u),fy=father(v); if (mem[fx].size>mem[fy].size) swap(fx,fy),swap(u,v); adde(u,v); adde(v,u); rec[]=; dfs(u,v); gi[fx]=fy; mem[fy].size+=mem[fx].size;
for (int i=;i<=;i++) {
for (int j=;j<=rec[];j++) {
int u=rec[j];
g[u][i]=g[g[u][i-]][i-];
}
} int t1=mem[fx].s,t2=mem[fx].t,t3=mem[fy].s,t4=mem[fy].t; int d1=getdist(t1,t3),d2=getdist(t1,t4);
int d3=getdist(t2,t3),d4=getdist(t2,t4);
int d5=getdist(t1,t2),d6=getdist(t3,t4); int num=,d=;
if (d1>d) num=,d=d1; if (d2>d) num=,d=d2;
if (d3>d) num=,d=d3; if (d4>d) num=,d=d4;
if (d5>d) num=,d=d5; if (d6>d) num=,d=d6; if (num==) mem[fy].s=t1,mem[fy].t=t3;
else if (num==) mem[fy].s=t1,mem[fy].t=t4;
else if (num==) mem[fy].s=t2,mem[fy].t=t3;
else if (num==) mem[fy].s=t2,mem[fy].t=t4;
else if (num==) mem[fy].s=t1,mem[fy].t=t2;
else if (num==) mem[fy].s=t3,mem[fy].t=t4; printf("%d\n",d);
}
return ;
}

C.cpp

HGOI20190813 省常中互测6的更多相关文章

  1. HGOI 20190816 省常中互测8

    Problem A  有两条以(0,0)为端点,分别经过(a,b),(c,d)的射线,你要求出夹在两条射线中间,且距离(0,0)最近的点(x,y) 对于$100\%$的数据满足$1 \leq T \l ...

  2. HGOI20190814 省常中互测7

    Problem A 中间值 对于$2$个非严格单增序列$\{A_n\} , \{B_n\}$,维护下列两个操作: 1 x y z: (x=0)时将$A_y = z$ , (x=1)时将$B_y = z ...

  3. HGOI20190811 省常中互测4

    Problem A magic 给出一个字符串$S$,和数字$n$,要求构造长度为$n$只含有小写字母的字符串$T$, 使得在$T$中存在删除且仅删除一个子串使得$S=T$成立. 输出$T$的构造方案 ...

  4. HGOI20190810 省常中互测3

    Problem A  夏洛特 若当前处在点$(x,y)$下一时刻可以向该点四周任意方向走动一步, 初始在$(0,0)$是否存在一条合法的路线满足下列$n$个限制: 每一个限制形如$t_i , x_i ...

  5. HGOI20190809 省常中互测2

    Problem A 时之终结 构造一个含有$n$个节点的无重边无自环的有向图, 使得从$1$出发,每一次经过一条$(u,v) (u < v)$的边到达节点$n$的方案恰好有$y$种. 对于$10 ...

  6. HGOI20190808 省常中互测1

    Problem A  sum 给出$n$个元素的序列$\{a_i\}$,求出两个不相交连续子序列的最大元素和. 即对于$1 \leq A \leq B \leq C \leq D \leq n$最大化 ...

  7. HGOI20190812 省常中互测5

    Task 1 辩论 有N 个参加辩论的候选人,每个人对这两个议题都有明确的态度,支持或反对.作为组织者,小D 认真研究了每个候选人,并给每个人评估了一个非负的活跃度,他想让活跃度之和尽可能大.选出的候 ...

  8. 【2018集训队互测】【XSY3372】取石子

    题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...

  9. 【CH 弱省互测 Round #1 】OVOO(可持久化可并堆)

    Description 给定一颗 \(n\) 个点的树,带边权. 你可以选出一个包含 \(1\) 顶点的连通块,连通块的权值为连接块内这些点的边权和. 求一种选法,使得这个选法的权值是所有选法中第 \ ...

随机推荐

  1. # 江西ccpc省赛-waves-(DP做法)

    江西ccpc省赛-waves-(DP做法) 题链:http://acm.hdu.edu.cn/showproblem.php?pid=6570 题意:给你长度为N,1≤N≤100000的一个数组,其中 ...

  2. Git服务器搭建与配置管理

    Git for Windows TortoiseGit:是一个开放的git版本控制系统的源客户端,支持Winxp/vista/win7.离不开真正的Git(Git for windows版本名字又叫M ...

  3. Java 类的构造器中this()和super()的困惑

    关于构造器中super的使用,书本上这样写: “super是指向父类的引用,如果构造方法没有显示地调用父类的构造方法,那么编译器会自动为它加上一个默认的super()方法调用.如果父类由没有默认的无参 ...

  4. 如何同步多个 git 远程仓库

    请看 -> 如何同步多个 git 远程仓库

  5. 剑指offer-孩子们的游戏(圆圈中最后剩下的数)-知识迁移能力-python

    题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指 ...

  6. Css常用的技巧

    一.使用css缩写 使用缩写可以帮助减少你CSS文件的大小,更加容易阅读.  具体内容请浏览:CSS常用缩写语法 二.明确定义单位,除非值为0. 忘记定义尺寸的单位是CSS新手普遍的错误.在HTML中 ...

  7. Linux小知识:rm -rf/*会将系统全部删除吗

    Linux小知识:rm -rf/*会将系统全部删除吗 本文是学习笔记,视频地址为:https://www.bilibili.com/video/av62839850 执行上面的命令并不会删除所有内容( ...

  8. web-CSS居中大全

    居中是我们使用css来布局时常遇到的情况.使用css来进行居中时,有时一个属性就能搞定,有时则需要一定的技巧才能兼容到所有浏览器,本文就居中的一些常用方法做个简单的介绍. 注:本文所讲方法除了特别说明 ...

  9. openstack云主机冷迁移

    1:开启nova计算节点之间互信 冷迁移需要nova计算节点之间使用nova用户互相免密码访问 默认nova用户禁止登陆,开启所有计算节点的nova用户登录shell. usermod -s /bin ...

  10. deep_learning_Function_matpotlib_scatter()函数

    plt.scatter()函数用于生成一个scatter散点图. matplotlib.pyplot.scatter(x, y, s=20, c='b', marker='o', cmap=None, ...