期望得分:30+50+30=110

实际得分:40+0+0=40

并查集合并再次写炸。。。

模拟更相减损术的过程

更相减损术,差一定比被减数小,当被减数=减数时,停止

对于同一个减数来说,会被减 第1次减这个减数的被减数/这个减数 次

然后这个减数成为被减数,减数变为 原被减数-k*原减数,即原被减数%原减数

就变成了辗转相除

#include<cstdio>
#include<iostream> using namespace std; typedef long long LL; LL ans; void gcd(LL a,LL b)
{
if(!b)
{
ans++;
return;
}
ans+=a/b;
gcd(b,a%b);
} int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
LL a,b;
cin>>a>>b;
gcd(a,b);
cout<<ans;
}

首先可以确定最终的销售度取决于最大生成树上的边

从大到小枚举 每一条边,直至加入了n-1 条边

对于每一条可以加入的边,不是直接像最大生成树那样直接连边

而是新建一个节点,这条边的两个点作为新节点的左右子节点

新节点的权值为这条边的重量限制

这样建出一颗二叉树,二叉树的叶子节点就是原来的城市

且二叉树的非叶子节点的权值 自底向上 递减

设二叉树非叶子节点k的左右子节点分别为l,r,对应的子树大小为siz,k的权值为w

那么它可以表示 在l 重量为w 的 车 比 重量 为w+1 的车 能多到siz[k]-siz[l]个城市

在r 重量为w的车比重量为w+1的长 能多到siz[k]-siz[r] 个城市

如果所有边的重量限制不一样,

那么每个城市的销售度的和=城市到根节点路径上相邻节点子树大小的差 的 平方和,前缀和统计即可

如果有的边的重量限制一样,

在父节点能到的城市,在这儿也能到,这对相邻节点就没有贡献,同时让这个点的siz=父节点的siz

#include<cstdio>
#include<iostream>
#include<algorithm> using namespace std; #define N 100001
#define M 500001 typedef long long LL; struct node
{
int u,v,w;
}e[M]; int tr[N<<][]; int fa[N<<],siz[N<<]; int val[N<<]; LL ans[N]; void read(int &x)
{
x=; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
} bool cmp(node p,node q)
{
return p.w>q.w;
} void dfs(int x,int fa,LL w)
{
LL delta=;
if(fa)
{
if(val[fa]==val[x]) siz[x]=siz[fa];
else delta=(LL)(siz[fa]-siz[x])*(siz[fa]-siz[x]);
}
if(tr[x][])
{
dfs(tr[x][],x,w+delta);
dfs(tr[x][],x,w+delta);
}
else ans[x]=w+delta;
} int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); } int main()
{
freopen("car6.in","r",stdin);
//freopen("car.out","w",stdout);
int n,m;
read(n); read(m);
for(int i=;i<=n;i++) fa[i]=i,siz[i]=;
for(int i=;i<=m;i++) read(e[i].u),read(e[i].v),read(e[i].w);
sort(e+,e+m+,cmp);
int tot=; int u,v;
int sum=n;
for(int i=;i<=m && tot!=n-;i++)
{
u=find(e[i].u); v=find(e[i].v);
if(u==v) continue;
tot++;
tr[++sum][]=u;
tr[sum][]=v;
fa[u]=fa[v]=fa[sum]=sum;
val[sum]=e[i].w;
siz[sum]=siz[u]+siz[v];
}
dfs(sum,,);
for(int i=;i<=n;i++) cout<<ans[i]<<' ';
}

50分暴力

从大到小加边,每加一条边暴力枚举统计 两个联通块的影响

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm> using namespace std; #define N 2001
#define M 20001 int n,m;
struct node
{
int u,v,w;
}e[M]; int front[N],nxt[M<<],to[M<<],tot; int fa[N],siz[N]; bool vis[N];
int use[N],q[N]; int ans[N][N]; void read(int &x)
{
x=; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
} bool cmp(node p,node q)
{
return p.w>q.w;
} void init()
{
read(n); read(m);
for(int i=;i<=m;i++) read(e[i].u),read(e[i].v),read(e[i].w);
sort(e+,e+m+,cmp);
for(int i=;i<=n;i++) fa[i]=i,siz[i]=;
} int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); } void bfs(int s,int w,int sum)
{
int head=,tail=,cnt=;
q[]=s; ans[s][w]+=sum; use[++cnt]=s; vis[s]=true;
while(head<tail)
{
for(int i=front[q[head++]];i;i=nxt[i])
if(!vis[to[i]])
{
vis[to[i]]=true;
ans[to[i]][w]+=sum;
q[tail++]=to[i];
use[++cnt]=to[i];
}
}
for(int i=;i<=cnt;i++) vis[use[i]]=false;
} void add(int u,int v)
{
to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
} void MST()
{
int u,v;
for(int i=;i<=m && tot!=n-;i++)
{
u=e[i].u; v=e[i].v;
if(find(u)==find(v)) continue;
tot++;
bfs(u,e[i].w,siz[fa[v]]);
bfs(v,e[i].w,siz[fa[u]]);
siz[fa[v]]+=siz[fa[u]];
fa[fa[u]]=fa[v];
add(u,v);
}
int out;
for(int i=;i<=n;i++)
{
out=;
for(int j=;j<N;j++)
out+=ans[i][j]*ans[i][j];
cout<<out<<' ';
}
} int main()
{
freopen("car.in","r",stdin);
freopen("car.out","w",stdout);
init();
MST();
}

模型:

求在有两种限制的情况下的最小值

定义f(x)表示完全满足其中一个限制,另一个限制为x时的最小值,

选取一个c,c满足 当cx-f(x) 最大时,x满足另一个限制

设cx-f(x)=s ,则 满足两个限制下的最小值=cx-s

具体到本题来说

两个限制:选k个数,选的数的距离>=m

设f(x)表示 选的数的距离>=m 时,选出x个数的最小值

二分一个c,c满足 当cx-f(x)最大时,x=k

设cx-f(x)=s,则选出k个数,每个数的距离>=m的最小值=cx-s

如何检验二分出的c?

令dp[i] 表示 到第i个数,选出的每个数的距离>=m 的最大值

f[i] 表示 在满足dp[i]时,最少取多少个数

不选,由i-1转移

选,由i-m 转移

若f[n]<=k , c下调,否则,c上调

c的意义:

c 是 f(x) 的斜率

当 cx-f(x)取得最大值时,若x=k,则

直线 cx 与  点(k+1,f(k+1))和点(k,f(k))构成的直线平行

当取值最大的x>=k 时,要下调c

取值最大位置<k 时,上调c

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm> using namespace std; #define N 1000001 int a[N]; int n,m; long long dp[N];
int f[N]; void read(int &x)
{
x=; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
} long long getsum(long long t)
{
memset(dp,,sizeof(dp));
for(int i=;i<=n;i++)
if(i<=m) dp[i]=max(dp[i-],t-a[i]);
else dp[i]=max(dp[i-],dp[i-m]+t-a[i]);
return dp[n];
} int check(long long mid)
{
memset(dp,,sizeof(dp));
memset(f,,sizeof(f));
for(int i=;i<=n;i++)
if(i<=m)
{
if(mid-a[i]>dp[i-]) dp[i]=mid-a[i],f[i]=;
else dp[i]=dp[i-],f[i]=f[i-]; }
else
{
if(dp[i-m]+mid-a[i]>dp[i-]) dp[i]=dp[i-m]+mid-a[i],f[i]=f[i-m]+;
else if(dp[i-m]+mid-a[i]==dp[i-]) dp[i]=dp[i-],f[i]=min(f[i-],f[i-m]+);
else dp[i]=dp[i-],f[i]=f[i-];
}
// cout<<mid<<' '<<f[n]<<'\n';
return f[n];
} int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
int k;
read(n); read(m); read(k);
for(int i=;i<=n;i++) read(a[i]);
long long l=,r=1LL**n,mid,c;
while(l<=r)
{
mid=l+r>>;
if(check(mid)>=k) c=mid,r=mid-;
else l=mid+;
}
cout<<c*k-getsum(c);
}

2017 清北济南考前刷题Day 4 afternoon的更多相关文章

  1. 2017 清北济南考前刷题Day 7 afternoon

    期望得分:100+100+30=230 实际得分:100+100+30=230 1. 三向城 题目描述 三向城是一个巨大的城市,之所以叫这个名字,是因为城市中遍布着数不尽的三岔路口.(来自取名力为0的 ...

  2. 2017 清北济南考前刷题Day 1 afternoon

    期望得分:80+30+70=180 实际得分:10+30+70=110 T1 水题(water) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK出了道水 ...

  3. 2017 清北济南考前刷题Day 3 afternoon

    期望得分:100+40+100=240 实际得分:100+40+100=240 将每个联通块的贡献乘起来就是答案 如果一个联通块的边数>点数 ,那么无解 如果边数=点数,那么贡献是 2 如果边数 ...

  4. 2017 清北济南考前刷题Day 6 afternoon

    期望得分:100+100+30=230 实际得分: 正解: 枚举最高的位,这一位m是1但实际用了0 然后剩余的低位肯定是 正数就用1,负数用0 考场思路:数位DP #include<cstdio ...

  5. 2017 清北济南考前刷题Day 5 afternoon

    期望得分:100+100+30=230 实际得分:0+0+0=30 T1 直接模拟 #include<cstdio> #include<iostream> using name ...

  6. 2017 清北济南考前刷题Day 2 afternoon

    期望得分:100+60+70=230 实际得分:0+60+0=60 T1 可以证明如果一对括号原本就匹配,那么这对括号在最优解中一定不会被分开 所以用栈记录下没有匹配的括号 最后栈中一定是 一堆右括号 ...

  7. 2017 清北济南考前刷题Day 3 morning

    实际得分:100+0+0=100 T1 右上角是必败态,然后推下去 发现同行全是必胜态或全是必败态,不同行必胜必败交叉 列同行 所以n,m 只要有一个是偶数,先手必胜 #include<cstd ...

  8. 2017 清北济南考前刷题Day 7 morning

    期望得分:100+50+20=170 实际得分:10+50+20=80 1. 纸牌 题目描述 在桌面上放着n张纸牌,每张纸牌有两面,每面都写着一个非负整数.你的邪王真眼可以看到所有牌朝上的一面和朝下的 ...

  9. 2017 清北济南考前刷题Day 6 morning

    T1 贪心 10 元先找5元 20元 先找10+5,再找3张5 #include<cstdio> using namespace std; int m5,m10,m20; int main ...

随机推荐

  1. CentOS7下安装MySQL的安装与配置(yum) (转)

    原文链接:http://www.centoscn.com/mysql/2016/0626/7537.html 1.配置YUM源 在MySQL官网中下载YUM源rpm安装包:http://dev.mys ...

  2. 谷歌chrome 插件(扩展)开发——基础篇

    公司需要开发chrome浏览器右键菜单功能,点击后可传页面的相关参数与客户端(winform)交互. 我对chrome扩展一无所知,所以第一阶段,我称之为"扫盲".也就是先找些相关 ...

  3. Gradle下载 Jar 包

    使用此方法下载Jar包的前提是已经配置好了Gradle的环境了,配置好的标志是在终端输入gradle不提示command not found. 1. 编写build.gradle文件代码: apply ...

  4. 原生jdbc操作mysql数据库详解

    首先给大家说一下使用JDBC链接数据库的步骤 1.加载链接数据库驱动 2.建立数据库链接 3.创建数据库操作对象 4.编写sql语句,执行sql语句 5.获取结果集 6.释放资源 我这边采用的是mav ...

  5. Flume - Kafka日志平台整合

    1. Flume介绍 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据:同时,Flume提供 ...

  6. 请详细描述(以硬盘启动)Linux系统从打开主机电源到进入登录界面整个过程的流程。

    1. 开机进行BIOS(BIOS(Basic Input / Output System)自检测系统外围硬件设备如CPU.内存.IO.显卡.鼠标键盘等.根据BIOS中设置的系统启动顺序搜索用于启动系统 ...

  7. 如何使用svn命令行更新想要的目录?

    内容来自网络. 一 某些原因想在svn co的时候排除某些目录,可以绕个圈子,分三步来完成:co外层目录:svn checkout --depth empty URL[URL[LOCATION]完成之 ...

  8. ajax利用FormData异步文件提交

    通常情况下,我们上传文件都会使用form表单来提交文件.但有时候,我们会有异步提交文件的需求,在这种情况下,我们就需要新建一个Formdata来提交文件,后台如果使用的是PHP的话可以使用$_FILE ...

  9. 笔记:Spring Cloud Hystrix 服务容错保护

    由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加 ...

  10. 笔记:Jersey REST API 设计

    REST 统一接口 REST 使用 HTTP 协议的通用方法作为统一接口的标准词汇,REST 服务所提供的方法信息都在 HTTP 方法里,每一种HTTP请求方法都可以从安全性和幂等性两方面考虑,这对正 ...