HGOI20190813 省常中互测6
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的更多相关文章
- HGOI 20190816 省常中互测8
Problem A 有两条以(0,0)为端点,分别经过(a,b),(c,d)的射线,你要求出夹在两条射线中间,且距离(0,0)最近的点(x,y) 对于$100\%$的数据满足$1 \leq T \l ...
- HGOI20190814 省常中互测7
Problem A 中间值 对于$2$个非严格单增序列$\{A_n\} , \{B_n\}$,维护下列两个操作: 1 x y z: (x=0)时将$A_y = z$ , (x=1)时将$B_y = z ...
- HGOI20190811 省常中互测4
Problem A magic 给出一个字符串$S$,和数字$n$,要求构造长度为$n$只含有小写字母的字符串$T$, 使得在$T$中存在删除且仅删除一个子串使得$S=T$成立. 输出$T$的构造方案 ...
- HGOI20190810 省常中互测3
Problem A 夏洛特 若当前处在点$(x,y)$下一时刻可以向该点四周任意方向走动一步, 初始在$(0,0)$是否存在一条合法的路线满足下列$n$个限制: 每一个限制形如$t_i , x_i ...
- HGOI20190809 省常中互测2
Problem A 时之终结 构造一个含有$n$个节点的无重边无自环的有向图, 使得从$1$出发,每一次经过一条$(u,v) (u < v)$的边到达节点$n$的方案恰好有$y$种. 对于$10 ...
- HGOI20190808 省常中互测1
Problem A sum 给出$n$个元素的序列$\{a_i\}$,求出两个不相交连续子序列的最大元素和. 即对于$1 \leq A \leq B \leq C \leq D \leq n$最大化 ...
- HGOI20190812 省常中互测5
Task 1 辩论 有N 个参加辩论的候选人,每个人对这两个议题都有明确的态度,支持或反对.作为组织者,小D 认真研究了每个候选人,并给每个人评估了一个非负的活跃度,他想让活跃度之和尽可能大.选出的候 ...
- 【2018集训队互测】【XSY3372】取石子
题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...
- 【CH 弱省互测 Round #1 】OVOO(可持久化可并堆)
Description 给定一颗 \(n\) 个点的树,带边权. 你可以选出一个包含 \(1\) 顶点的连通块,连通块的权值为连接块内这些点的边权和. 求一种选法,使得这个选法的权值是所有选法中第 \ ...
随机推荐
- 什么是云数据库 HBase 版
云数据库 HBase 版(ApsaraDB for HBase)是基于 Hadoop 的一个分布式数据库,支持海量的PB级的大数据存储,适用于高吞吐的随机读写的场景.目前在阿里内部有数百个集群,100 ...
- 【pytorch】学习笔记(一)-张量
pytorch入门 什么是pytorch PyTorch 是一个基于 Python 的科学计算包,主要定位两类人群: NumPy 的替代品,可以利用 GPU 的性能进行计算. 深度学习研究平台拥有足够 ...
- C++游戏服务器编程笔记 IP详解
C++游戏服务器编程笔记 IP详解 IP详解 INTERNET的历史 上世纪60年底起源于美国 1992年,Internet上的主机超过了100万台 现在已经是现代文明人的必需品 TCP/IP的 ...
- # 江西CCPC省赛-Rng(概率+逆元)
江西CCPC省赛-Rng(概率+逆元) 题意: 给出一个n,在[1,n]之间选一个R1,在[1,R1]之间选一个L1,得到区间[L1,R1],同理获取区间[L2,R2],问两个区间相交的概率对1e9+ ...
- 使用Redis實現秒殺功能
<?php $id = 1; $pdo=new PDO("mysql:host=127.0.0.1;dbname=test","root","r ...
- Spring的基本应用(1):依赖以及控制反转
在说到这里的时候,首先要说下程序的耦合和解耦,以便对上节做一个解释. 一.程序的耦合和解耦 1.程序的耦合性(Copling) (1)程序的耦合性,也叫做耦合度,是对模块之间关联程度的度量,耦合性的强 ...
- python之json操作
1.json.dumps()用于将dict类型的数据转成str 备注:文件路径前面加上 r 是为了避免转义 1 import json 2 3 dict = {'a': 'wo', 'b': 'zai ...
- C#程序集及程序集概念介绍
一.源代码-面向CLR的编译器-托管模块-(元数据&IL代码)中介绍了编译器将源文件编译成托管模块(中间语言和元数据),本文主要介绍如何将托管模块合并成程序集. 1.程序集的基本概念 2.程序 ...
- Proxy.newInstance与InvocationHandler的使用示例
先定义一个接口,根据代理模式的原理,被代理类与代理类都要实现它. public interface Person { void eat(); } 再写一个实际执行任务的类(被代理类): public ...
- ffmpeg3.3.2命令行参数笔记
组成: 1.libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能,包含demuxers和muxer库: 2.libavcodec:用 ...