HZOJ 单
两个子任务真的是坑……考试的时候想到了60分的算法,然而只拿到了20分(各种沙雕错,没救了……)。
算法1:
对于测试点1,直接n遍dfs即可求出答案,复杂度O(n^2),然而还是有好多同学跑LCA/最短路……
期望得分10;
算法2(搬运题解,因为这个我没有想到……):
t=1的数据最直接的想法是枚举所有可能的a[]数组判断是否可行.第2个测试点n<=5,1<=a[i]<=20.注意20^5=3200000,直接暴力搜索a[i]的取值是可以承受的,可以通过第2个测试点,期望得分10分,结合算法1,期望得分20分.
算法3:
对于测试点6,7。我们可以发现每个点的dis都有联系,这不就是二次扫描与换根吗(具体看代码理解)。结合算法2期望得分40;
void dfs2(int x,int fa)
{
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
b[v(i)]=b[x];
b[v(i)]-=sum[v(i)];
b[v(i)]+=sum[x]-sum[v(i)];
sum[x]-=sum[v(i)];
sum[v(i)]+=sum[x];
dfs2(v(i),x);
sum[v(i)]-=sum[x];
sum[x]+=sum[v(i)];
}
}
第二次扫描的代码
算法4:
t=1的数据中,b数组的表达式写出来之后,每个b[i]对应一个关于a[i]和树上两点距离的方程,例如b[1]=dis(1,1)*a[1]+dis(1,2)*a[2]+dis(1,3)*a[3]+...+dis(1,n)*a[n],于是可以列出n个方程用高斯消元求解,可以通过第2,3,4个测试点,期望得分30分,结合算法3,期望得分60分。要注意的一点是,高斯消元中要注意这句话‘if(fabs(sa[j][i])>fabs(sa[i][i]))’,本人死于此……还有不少人死于强制类型转化的四舍五入,其实printf("%0.0lf")即可;
之后的算法题解写的已经很清晰了,我觉得我也写不了这么清楚,就直接搬运了。
算法5:
对于测试点5,树退化为1条链。这个测试点的作用主要在于启发选手想到正解的思路(然而我并没有什么启发……)。
t=0时,我们分别考虑编号小于i和大于i的点对b[i]的贡献.那么离得越远的点对答案的贡献越大.分别考虑每条边对答案的贡献,那么左侧的某条边对答案的贡献就是这条边左侧全部a[i]之和.实际上,我们对a[i]分别求取两次前缀和,两次后缀和即可完成对b[i]的计算.
记suf(i)为a[i]+a[i+1]+....+a[n-1]+a[n],pre(i)为a[1]+a[2]+...+a[i-1]+a[i],则b[i]=pre(1)+pre(2)+...+pre(i-1)+suf(i+1)+suf(i+2)+...+suf(n-1)+suf(n)
考虑t=1的情况.
我们知道suf(2)+suf(3)+...+suf(n)的值(即b[1]),知道pre(1) + suf(3) + suf(4) +...+suf(n)的值(即b[2]),知道pre(1)+pre(2)+suf(4)+...+suf(n)的值(即b[3]),注意到这些式子有很多项是一样的,考虑作差.可以得到下面的式子:
b[2]-b[1]=pre(1)-suf(2),b[3]-b[2]=pre(2)-suf(3).....b[i+1]-b[i]=pre(i)-suf(i+1)
这些式子是有实际意义的,考虑从b[1]变到b[2]时答案的变化量,变化的就是1和2之间连边的贡献.
同时,记SUM=a[1]+a[2]+...+a[n-1]+a[n],显然pre(i)+suf(i+1)=SUM,因此pre(i)=SUM-suf(i+1),将上面式子中所有pre换成suf,我们就知道了
SUM-2*suf(2) ,SUM-2*suf(3)...SUM-2*suf(n)的取值。
注意我们将n个式子作差之后得到了n-1个等式,实际上是丢弃了一些信息,只保留了b[i]之间的相对大小而忽略了b[i]自身的数值大小.于是考虑b[1]=suf(2)+suf(3)+suf(4)+... +suf(n),我们发现suf(2)到suf(n)都恰好出现了一次,而之前得到了n-1个形如SUM-2*suf(i)的式子,将这n-1个式子相加,suf(2),suf(3)...suf(n)前面的系数都是2,SUM的系数为(n-1),那么把这个式子加上2*b[1],就得到了(n-1)*SUM,将求得的SUM代入之前的n-1个式子可以得到suf(2),suf(3),suf(4)......suf(n),差分一下即可得到a数组.
时间复杂度O(n),可以通过第5个测试点.推出这个做法,树上的做法也就不难想了.
算法6(满分算法):
考虑树上的问题.
t=0的时候,我们可以先暴力计算出b[1],然后从b[1]出发开始dfs,由某个点的父亲的b[i]推出这个点的b[i],变化的是这个点和父亲的连边的贡献,也就是这条边两侧的点的a[i]之和的差值.
t=1的时候,顺着链上的思路,我们先随便找一个点为根建树,将有边直接相连的两个点的b[i]作差.设x的父亲为prt[x],以x为根的子树中所有a[i]之和为sum(x), SUM=a[1]+a[2]+...+a[n-1]+a[n],那么b[x]-b[prt[x]]=(SUM-sum(x))-sum(x).
同链的情况一样,得到n-1个式子,将树根的b[i]也列出式子,可以求出全部a[i]之和,然后就可以根据之前的式子推出所有的a[i],和链的做法类似,不再赘述.
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define MAXN 100010
#define int LL
#define LL long long
#define esp 1e-8
#define ma(x) memset(x,0,sizeof(x))
using namespace std;
struct edge
{
int u,v,nxt;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].nxt
}ed[MAXN*];
int first[MAXN],num_e;
#define f(x) first[x]
int T,n,t;
LL a[MAXN],b[MAXN];
LL dis[MAXN],sum[MAXN];
LL SUM,suf[MAXN];
LL SUM_2suf[MAXN];
LL SUM_2sum[MAXN];
LL TSUM=; int map[][];
double sa[][],sb[];
void GS()
{
for(int i=;i<=n;i++)
{
for(int j=i;j<=n;j++)
if(fabs(sa[j][i])>fabs(sa[i][i]))
{
for(int k=;k<=n;k++)
swap(sa[i][k],sa[j][k]);
swap(sb[i],sb[j]);
}
for(int j=;j<=n;j++)
{
if(j==i)continue;
if(fabs(sa[i][i])<=esp)continue;
double cal=sa[j][i]/sa[i][i];
for(int k=;k<=n;k++)
sa[j][k]-=sa[i][k]*cal;
sb[j]-=sb[i]*cal;
}
}
}
void dfs(int x,int fa)
{
sum[x]=a[x];
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dis[v(i)]=dis[x]+;
dfs(v(i),x);
sum[x]+=sum[v(i)];
}
}
void dfs2(int x,int fa)
{
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
b[v(i)]=b[x];
b[v(i)]-=sum[v(i)];
b[v(i)]+=sum[x]-sum[v(i)];
sum[x]-=sum[v(i)];
sum[v(i)]+=sum[x];
dfs2(v(i),x);
sum[v(i)]-=sum[x];
sum[x]+=sum[v(i)];
}
}
void dfs3(int x,int fa)
{
if(x!=)SUM_2sum[x]=b[x]-b[fa];
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
dfs3(v(i),x);
}
void dfs4(int x,int fa)
{
if(x==)sum[x]=SUM;
else sum[x]=(SUM-SUM_2sum[x])/;
a[x]=sum[x];
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
dfs4(v(i),x);
a[x]-=sum[v(i)];
}
}
inline int read();
inline void add(int u,int v);
signed main()
{
// freopen("in.txt","r",stdin); T=read();
while(T--)
{
ma(sum);ma(first);num_e=;ma(dis);ma(a);ma(b);
ma(map);ma(sa);ma(sb);ma(suf);SUM=;ma(SUM_2suf);
ma(SUM_2sum);TSUM=;
bool pd=;
n=read();int u,v;
for(int i=;i<n;i++)
{
u=read(),v=read();
if(v!=u+)pd=;
add(u,v),add(v,u);
}
t=read();
if(t==)
{
for(int i=;i<=n;i++)a[i]=read();
if(n<=)
{
for(int i=;i<=n;i++)
{
dis[i]=;
dfs(i,);
for(int j=;j<=n;j++)b[i]+=a[j]*dis[j];
}
for(int i=;i<=n;i++)
printf("%lld ",b[i]);
puts("");
continue;
}
else
{
dis[]=;dfs(,);
for(int i=;i<=n;i++)b[]+=dis[i]*a[i];
dfs2(,);
for(int i=;i<=n;i++)
printf("%lld ",b[i]);
puts("");
continue;
}
}
if(t==)
{
for(int i=;i<=n;i++)b[i]=read();
if(n<=)
{
for(int i=;i<=n;i++)
{
dis[i]=;dfs(i,);
for(int j=;j<=n;j++)map[i][j]=dis[j];
}
for(int i=;i<=n;i++)sb[i]=b[i];
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)sa[i][j]=map[j][i];
GS();
for(int i=;i<=n;i++)
printf("%0.0lf ",sb[i]/sa[i][i]);
puts("");
continue;
}
if(pd)
{
for(int i=;i<=n;i++)SUM_2suf[i]=b[i]-b[i-];
LL tem=;
for(int i=;i<=n;i++)tem+=SUM_2suf[i];
tem+=b[]*;
SUM=tem/(n-);
for(int i=;i<=n;i++)suf[i]=(SUM-SUM_2suf[i])/;
suf[]=SUM;suf[n+]=;
for(int i=;i<=n;i++)a[i]=suf[i]-suf[i+];
for(int i=;i<=n;i++)
printf("%lld ",a[i]);
puts("");
continue;
}
else
{
dfs3(,);
for(int i=;i<=n;i++)TSUM+=SUM_2sum[i];
TSUM+=b[]*;
SUM=TSUM/(n-);
dfs4(,);
for(int i=;i<=n;i++)
printf("%lld ",a[i]);
puts("");
continue;
}
}
}
}
inline int read()
{
int s=;char a=getchar();
while(a<''||a>'')a=getchar();
while(a>=''&&a<=''){s=s*+a-'';a=getchar();}
return s;
}
inline void add(int u,int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
HZOJ 单的更多相关文章
- HZOJ 20190727 T2 单(树上dp+乱搞?+乱推式子?+dfs?)
考试T2,考试时想到了40pts解法,即对于求b数组,随便瞎搞一下就oxxk,求a的话,很明显的高斯消元,但考试时不会打+没开double挂成10pts(我真sb),感觉考试策略还是不够成熟,而且感觉 ...
- H5单页面手势滑屏切换原理
H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transition)来实现的,效果图如下所示,本文简单说一下其实现原理和主要思路. 1.实现原理 ...
- 快速构建H5单页面切换骨架
在Web App和Hybrid App横行的时代,为了拥有更好的用户体验,单页面应用顺势而生,单页面应用简称`SPA`,即Single Page Application,就是只有一个HTML页面的应用 ...
- ASP.NET Aries 入门开发教程9:业务表单的开发
前言: 经过前面那么多篇的列表的介绍,终于到了大伙期待的表单开发了. 也是本系列的最后一篇文章了! 1:表单页面的权限设置与继承 对于表单页面,权限的设置有两种: 1:你可以选择添加菜单(设置为不显示 ...
- 【CSS进阶】伪元素的妙用--单标签之美
最近在研读 <CSS SECRET>(CSS揭秘)这本大作,对 CSS 有了更深层次的理解,折腾了下面这个项目: CSS3奇思妙想 -- Demo (请用 Chrome 浏览器打开,非常值 ...
- bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序
也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式.通常的说法是它通过避免页面刷新大大提高了网站的响应性,像操作桌面应用程序 ...
- 探索ASP.NET MVC5系列之~~~3.视图篇(下)---包含常用表单和暴力解猜防御
其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...
- SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)
SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...
- jQuery学习之路(8)- 表单验证插件-Validation
▓▓▓▓▓▓ 大致介绍 jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求.该插件捆绑了一套有用的验证方法,包括 ...
随机推荐
- C语言实现常用数据结构——图
#include<stdio.h> #include<stdlib.h> #define SIZE 20 #define LENGTH(a) (sizeof(a)/sizeof ...
- ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理
我们了解ABP框架内部自动记录审计日志和登录日志的,但是这些信息只是在相关的内部接口里面进行记录,并没有一个管理界面供我们了解,但是其系统数据库记录了这些数据信息,我们可以为它们设计一个查看和导出这些 ...
- .Net for Spark 实现 WordCount 应用及调试入坑详解
.Net for Spark 实现WordCount应用及调试入坑详解 1. 概述 iNeuOS云端操作系统现在具备物联网.视图业务建模.机器学习的功能,但是缺少一个计算平台产品.最近在调研使用 ...
- 这个注解一次搞定限流与熔断降级:@SentinelResource
在之前的<使用Sentinel实现接口限流>一文中,我们仅依靠引入Spring Cloud Alibaba对Sentinel的整合封装spring-cloud-starter-alibab ...
- 四种途径提升RabbitMQ传输数据的可靠性
前言 RabbitMQ虽然有对队列及消息等的一些持久化设置,但其实光光只是这一个是不能够保障数据的可靠性的,下面我们提出这样的质疑: (1)RabbitMQ生产者是不知道自己发布的消息是否已经正确达到 ...
- Git的忽略、分支、分支与主线的合并、远程仓库的操作
如果想了解 Git 以及一些基础命令的使用,请看我的另一篇博客: http://www.cnblogs.com/haojun/p/7797508.html 这篇博客会跟大家介绍一下怎么在提交的时候忽略 ...
- ajax入门级
AJAX AJAX:即异步的JavaScript 和 XML,是一种用于创建快速动态网页的技术: 传统的网页(不使用AJAX)如果需要更新内容,必需重载整个网页面: 使用AJAX则不与要加载更新整个网 ...
- Java基础知识了解
第一章 开发前言 一.java语言概述 Java是当下最流行的一种编程语言,至今有20年历史了.Java语言之父是James Gosling. Java是Sun公司(Stanford Universi ...
- [Qt]自定义表头实现过滤功能
1. 写在前面 过滤功能源自项目上交互优化用户体验,在表头添加过滤符号实现过滤,替换以往在表格上方占用一行过滤项进行过滤. 2. 过滤提示 过滤提示就是三态图标(normal,hover,press) ...
- core文件生成和路径设置
在程序崩溃时,内核会生成一个core文件,即程序最后崩溃时的内存映像,和程序调试信息. 之后可以通过gdb,打开core文件察看程序崩溃时的堆栈信息,可以找出程序出错的代码所在文件和函数. 1.cor ...