Problem A 矩阵第K小数

给定一个$n \times m$的矩阵,位置$A_{i,j}  = i\times j$,

给出$Q$个询问,每一次查询矩阵中第$Q_i$小的数是多少。

对于100%的数据 , $1 \leq n,m \leq 10^9 , Q \leq 100 , 1 \leq Q_i \leq n\times m$

Sol : 本题采用暴力模拟的复杂度是不能通过的,并且其矩阵的排布都是有规律的。

   第$i$行构成的数列是公差为$i$的等差数列。 可以考虑枚举每一行,然后就可以O(1) 计算这一行有多少比某一数$x$小了。

  同时,对于每一个询问二分答案,然后可以扫一遍$n$然后二分计算每一行的贡献。这样的复杂度就是$O(Qnlog_{2} (nm) )$

  同时考虑,每一行贡献是类似于$\lfloor \frac{x}{i} \rfloor$的东西,我们会发现这个东西对于$i$递增,它可能的取值只会有$\sqrt{x}$种。

  并且其值是单调下降的。 所以我们可以$O(\sqrt{n})$每次遍历每一个"平台"。但是由于需要二分答案,这样的复杂度会变成$log_2 n \sqrt{n}$

  总时间复杂度会变成$O(Q\sqrt{n} log_{2} (nm) log_2 n)$无法通过。

  如何在$O(\sqrt{n})$的复杂度遍历每一个块,然后统一计算是本题的瓶颈。

  问题等价于求$\sum\limits_{i=1}^n \lfloor \frac{x}{i} \rfloor $,我们可以考虑一个$i$满足$a = \lfloor \frac{x}{i} \rfloor $

  在某一个区间内,有$a \leq \lfloor \frac{x}{i} \rfloor < a+1 $ ,所以可以解得此时的$i \in  [\left \lfloor \frac{x}{\left \lfloor \frac{x}{i}  \right \rfloor+1} \right \rfloor + 1, \left \lfloor \frac{x}{\left \lfloor \frac{x}{i} \right \rfloor} \right \rfloor]$

  所以最后的复杂度就可以做到$O(Qlog_2 (nm) \sqrt{n} )$ 了。

# pragma GCC optimize()
# include <bits/stdc++.h>
# define int long long
using namespace std;
int n,m,q;
inline int read()
{
int X=,w=; char c=;
while(c<''||c>'') {w|=c=='-';c=getchar();}
while(c>=''&&c<='') X=(X<<)+(X<<)+(c^),c=getchar();
return w?-X:X;
}
int calc(int i,int x){
if (x%i==) return min(x/i-,m);
else return min(x/i,m);
}
bool check(int x,int k)
{
int ret=;
for (int l=,r;l<=min(x,m);l=r+) {
r=x/(x/l);
ret+=min((x/l),n)*(r-l+);
}
return (ret>=k);
}
signed main()
{
n=read();m=read();q=read();
while (q--) {
int x=read();
int l=,r=n,ans;
while (l<=r) {
int mid=(l+r)>>;
if (check(mid,x)) ans=mid,r=mid-;
else l=mid+;
}
printf("%lld\n",ans);
}
return ;
}

mat.cpp

Problem B 最小极差生成树

给出$n$个点$m$条边的连通图$G=(V,E)$,求出一棵生成树使得其最大边权和最小边权的差最小。

对于100%的数据$1 \leq m\leq 4000 ,  1 \leq n \leq 1000$

Sol : 枚举每一条边,以这条边权$s$为下界跑最小生成树,找出最小生成树的最大边权为$d$.

  用$d-s$更新答案。注意可能会有不合法情况,输出-1即可。

# pragma GCC optimize()
# include <bits/stdc++.h>
using namespace std;
const int N=4e3+;
int n,m,f[N];
struct node{ int u,v,w;}E[N];
bool cmp(node a,node b) {return a.w<b.w;}
inline int read()
{
int X=,w=; char c=;
while(c<''||c>'') {w|=c=='-';c=getchar();}
while(c>=''&&c<='') X=(X<<)+(X<<)+(c^),c=getchar();
return w?-X:X;
}
int father(int x) {
if (f[x]==x) return x;
return f[x]=father(f[x]);
}
int MST(int W)
{
for (int i=;i<=n;i++) f[i]=i;
int tmp=,o=;
for (int i=;i<=m;i++) {
if (E[i].w<W) continue;
int fx=father(E[i].u),fy=father(E[i].v);
if (fx==fy) continue;
tmp++; f[fx]=fy; o=max(o,E[i].w);
if (tmp==n-) break;
}
if (tmp!=n-) return -;
else return o;
}
int main()
{
int T=read();
while (T--) {
n=read();m=read();
for (int i=;i<=m;i++) {
int u=read(),v=read(),w=read();
E[i]=(node){u,v,w};
}
sort(E+,E++m,cmp);
int ans=-;
for (int i=;i<=m;i++) {
int t=MST(E[i].w);
if (t==-) continue;
if (ans==-) ans=t-E[i].w;
else ans=min(ans,t-E[i].w);
}
printf("%d\n",ans);
}
return ;
}

min.cpp

Problem C 数字分组

个体互异而值可能相同$n$个数$a_i$划分为若干组。 对于某一组记下这组元素的极差(最大数-最小数)

一种合法的划分要求每一组元素极差的和有$\leq M$的限制。求出所有合法划分的总数$mod \ 10^9 + 7$的值。

对于20%的数据 ,$1 \leq n \leq 10$ ; 对于另外20%的数据,$M=0$

对于100%的数据,$1 \leq n \leq 200,0 \leq M \leq 1000,1 \leq a_i \leq 500 $

Sol : 对于20%数据直接第二类斯特林数求和然后乘起来即可。

  对于100%的数据考虑DP。

  先对数据排序,记$f_{i,j,k}$表示前$i$个数,还有$j$个划分集可以添加元素,当前差值和为$k$方案总数。

  首先,显然即将添加的数$a_i$一定比之前添加的所有数要大,所以对于每一个没有完全划分好的集合都有贡献。首先计算当前差值的和是多少,我们会向每一个没有划分好的集合中添加一个差值$a_i - a_{i-1}$ , 所以当前差值的和就是$k + (a_i - a_{i-1})\times j$.

  这是因为每一组的最值差就是这一组当中最大值和最小值之间所有数的差的和 。

  所以这$j$组由于没有被划分好,就需要向里面添加这个差值$a_i - a_{i-1}$

  考虑新添加这个元素作为一个新的分组的开始:$f_{i,j+1,v} += f_{i-1,j,k}$

  考虑新添加的这个元素作为一个新的分组的开始和结束:$f_{i,j,v} += f_{i-1,j,k}$

  考虑新添加的元素作为之前一个旧的还没划分好的组的非结束的元素:$f_{i,j,v} += f_{i-1,j,k}\times j$ ($j \neq  0 $)

  考虑新添加的元素作为之前一个旧的还没划分好的组的结束元素$f_{i,j-1,v} += f_{i-1,j,k}\times j$ ($j \neq  0 $)

  注意到这里枚举的状态$k$表示的是由那一维状态转移过来的状态$k$

  用滚动数组实现就可以排除空间限制。

  复杂度$O(n^2k)$

# pragma GCC optimize()
# include <bits/stdc++.h>
# define int long long
using namespace std;
const int mo=1e9+;
int f[][][];
int a[];
int n,w;
signed main()
{
scanf("%lld%lld",&n,&w);
for (int i=;i<=n;i++) scanf("%lld",&a[i]);
sort(a+,a++n);
int p=; f[p][][]=;
for (int i=;i<=n;i++) {
p^=; memset(f[p],,sizeof(f[p]));
for (int j=;j<=i;j++)
for (int k=;k<=w;k++) {
int v=(a[i]-a[i-])*j+k;
if (v>w) continue;
f[p][j+][v]+=f[p^][j][k]; f[p][j+][v]%=mo;
f[p][j][v]+=f[p^][j][k];f[p][j][v]%=mo;
if (j) {
f[p][j][v]+=f[p^][j][k]*j; f[p][j][v]%=mo;
f[p][j-][v]+=f[p^][j][k]*j;f[p][j-][v]%=mo;
}
}
}
int ret=;
for (int k=;k<=w;k++)
ret+=f[p][][k],ret%=mo;
printf("%lld\n",ret);
return ;
}

grp.cpp

最新文章

  1. 解除win7网络限速.
  2. [译]IIS 8.0应用初始化
  3. js 滚动的文字(走马灯)
  4. HTTP 笔记与总结(4 )socket 编程:批量发帖
  5. JAVA 23种设计模式(转)
  6. mysql kill操作
  7. 深入理解移动web开发之PPI,Pixel,DevicePixelRatio(转)
  8. SCM管理器
  9. 项目实战7—Mysql实现企业级数据库主从复制架构实战
  10. MySQL分组、链接的使用
  11. angular6 导出json数据到excal表
  12. 12 python 初学(深浅拷贝、集合)
  13. 发现一个强大的可视化第三方库pyecharts
  14. 第18月第2天 ios博客
  15. 逆袭之旅DAY16.东软实训.Oracle.匿名块
  16. Oracle常用命令-用户、表空间、赋权限、导入导出
  17. ztree插件的使用及列表项拖拽的实现(jQuery)+异步加载节点数据
  18. SharePoint自动化系列——通过PowerShell创建SharePoint List Items
  19. scala的基础部分
  20. Spring Boot 的项目打包成的 JAR 包,制作成 docker 镜像并运行

热门文章

  1. spring笔记3路径跳转
  2. oracle 插入数据之坑--------oracle字符类型varchar2一个中文占多少字节
  3. # 深圳杯D题爬取电视收视率排行榜
  4. Python模拟进度条
  5. ZuulServlet源码分析及ZuulFilter加载
  6. django自带登录认证与登录自动跳转
  7. CentOs 7.6 开启防火墙后 无法显示远程文件夹
  8. [转载]java匿名对象
  9. LintCode 68---Binary Tree Postorder Traversal
  10. 07 Python之协程