HihoCoder 1562 ⼩Hi的钟表
⼩Hi喜欢各种⾓度。⼀天,他注意到了钟表上的⾓度,于是他想考考他的好朋友⼩Ho:对于⼀个24⼩时制的时刻,在 t 秒之后,对应在钟表上时针与分针的夹⾓是多少。为保证答案的唯⼀性,只需考虑不超过180°的⾓。你能帮助⼩Ho解决这个问题吗?
例如,下图可表⽰15点30分0秒经过0秒后的时间,其对应的夹⾓为75°和285°,在这个问题中我们只考虑不超过180°的⾓,所以此时的夹⾓为75°。
输⼊包含多组测试数据。
第⼀⾏为测试数据的组数T(1 ≤ T ≤ 1000)
对于每组数据:
第⼀⾏包含三个整数h,m,s,表⽰给定时刻的时、分、秒(0 ≤ h ≤ 23,0 ≤ m ≤ 59,0 ≤ s ≤ 59)
第⼆⾏包含⼀个整数t,表⽰经过的秒数(0 ≤ t ≤ 1000000000)
对于每组数据,输出时针与分针的夹角。四舍五⼊保留⼩数点后四位。
3
15 30 0
0
14 30 0
3600
15 30 0
3600
样例输出
75.0000
75.0000
45.0000
#include
#include
#include
#include
using namespace std;
const int mod = 60 * 60 * 12;
int main()
{
int t,h,m,s,time;
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d %d",&h,&m,&s,&time);
time = time + (h * 60 + m) * 60 + s;
time %= mod;
double h1 = (double)time / (3600 * 12);
double m1 = (double)time / 3600;
m1 = m1 - (int)m1;
double ans = fabs(h1 - m1) * 360;
if(ans > 180) ans = 360 - ans;
printf("%.4f\n",ans);
}
return 0;
}
HihoCoder 1564 同步H公司的终端
H公司有 N 台服务器,编号1~N,组成了一个树形结构。其中中央服务器处于根节点,终端服务器处于叶子节点。
中央服务器会向终端服务器发送消息。一条消息会通过中间节点,到达所有的终端服务器。消息每经过一台服务器(包括根节点的中央服务器和叶子节点的终端服务器),就会被处理并存储下来,其中编号为 i 的服务器,执行处理和存储这个过程会花费 Ai 毫秒的时间。消息从一台服务器传输到另一台服务器的时间可以忽略不计。
由于 Ai 各不相同,从中央服务器发出消息到各个终端处理并储存完毕的时间也是不相同的。例如如下的网络,假设1是中央服务器,而2和4是终端服务器。
1
/ \
2 3
|
4
如果A1=1, A2=4, A3=3, A4=2,那么假设1发出消息的时刻是0,终端2处理完的时刻是A1+A2=5,而终端4处理完的时刻是A1+A3+A4=6。
CEO交给小Ho一个任务,要求每台终端处理完消息的时间是完全一致的。小Ho想到了一个天才的解决方案:强行增加某些服务器的处理时间 Ai。例如在上例中,将 A2从4增加到5即可使所有终端都在6毫秒后处理完毕。
当然小Ho希望所有服务器增加的处理时间总和最少。你能帮助小Ho吗?
第一行包含一个整数 N。
第二行包含N个整数,A1, A2, ... AN,表示每个节点处理信息花费的时间。
以下 N-1行每行包含两个整数 a 和 b,表示 a 是 b 的父节点。
对于30%的数据,满足 1 ≤ N ≤ 103
对于100%的数据,满足1 ≤ N ≤ 105, 1 ≤ Ai ≤ 105
输出一个整数表示增加的处理时间之和最小是多少。
4
1 1 1 1
1 2
1 3
3 4
样例输出
1
自己的想法:
增加时间的节点应尽量靠上。
预处理:我们可以把整棵树的 DFS 序存下来(pos[i]),每个点的权值按 DFS 序存了(dfs[i]),DFS时记录下来每个点(以它为出发点到叶子结点)路径的最大的权值和( a[i] )以及每个点作为根的子树中的节点数(sum[i]),并将出发点的最大权值和记为 ma。把它的 DSF 序权值写进线段树里(线段树维护区间最值,进行区间加减)。
由于以一个点为根的子树中的节点DFS都是连续的,所以给它的子树中的节点都加时间可以简化为在区间[pos[u],pos[u] + sum[u] - 1]加值。
求解:再进行一次DFS,每到一个点给答案加上 temp(= ma - 该点的最大权值和),再给线段树区间的[pos[u],pos[u] + sum[u] - 1]加上 temp(相当于给途径它的终端都加上这个时间)。这样可以保证选尽量靠上的点,来增加尽量多的时间。输出答案即可。
附上代码(确实复杂了点。。。就当练习线段树了):
#include
#include
#include
#include
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
const ll INF = -1;
typedef struct node
{
ll data,lazy;
}node;
ll Max(ll x,ll y)
{
return x > y ? x : y;
}
node s[maxn << 2];
ll pos[maxn],sum[maxn],dfs[maxn],in[maxn],a[maxn];
ll ma = 0,n;
ll cal,solve,sta;
// 建图
vector
ll DFS1(ll x,ll ss)
{
//printf("x=%d\n",x);
int len = v[x].size();
for(int i = 0;i < len; ++i)
{
int u = v[x][i];
a[x] = max(a[x],DFS1(u,ss + a[u]));
}
if(len == 0) a[x] = ss;
return a[x];
}
// 记录树的DFS序
ll DFS2(ll x)
{
int len = v[x].size();
for(int i = 0;i < len; ++i)
{
int u = v[x][i];
pos[u] = cal,dfs[cal++] = a[u];
sum[x] += DFS2(u);
}
return sum[x];
}
// 线段树执行DFS序的区间加减并维护区间最值
void Pushup(int d,int lson,int rson)
{
s[d].data = max(s[lson].data,s[rson].data);
}
void Pushdown(int d,int lson,int rson,int l,int r)
{
ll temp = s[d].lazy;
s[d].lazy = 0;
s[lson].lazy += temp,s[rson].lazy += temp;
s[lson].data += temp,s[rson].data += temp;
}
void Build(int d,int l,int r)
{
if(l == r)
{
s[d].lazy = dfs[l];
s[d].data = dfs[l];
return ;
}
int m = l + (r - l) / 2, lson = d << 1,rson = d << 1 | 1;
s[d].lazy = 0;
Build(lson,l,m);
Build(rson,m + 1,r);
Pushup(d,lson,rson);
}
void Update(int d,int l,int r,int x,int y,int v)
{
if(l >= x && r <= y)
{
s[d].lazy += v;
s[d].data += v;
return;
}
int m = l + (r - l) / 2, lson = d << 1,rson = d << 1 | 1;
if(s[d].lazy) Pushdown(d,lson,rson,l,r);
if(x <= m) Update(lson,l,m,x,y,v);
if(m < y) Update(rson,m + 1,r,x,y,v);
Pushup(d,lson,rson);
}
ll Query(int d,int l,int r,int x,int y)
{
ll ans = INF;
if(l >= x && r <= y)
{
return s[d].data;
}
int m = l + (r - l) / 2, lson = d << 1,rson = d << 1 | 1;
if(s[d].lazy) Pushdown(d,lson,rson,l,r);
if(x <= m) ans = Max(ans,Query(lson,l,m,x,y));
if(m < y) ans = Max(ans,Query(rson,m + 1,r,x,y));
Pushup(d,lson,rson);
return ans;
}
// 求解
void Solve(int x)
{
int len = v[x].size(),temp,l,r;
for(int i = 0;i < len; ++i)
{
int u = v[x][i];
l = pos[u],r = l + sum[u] - 1;
temp = Query(1,1,n,l,r);
solve += (ma - temp);
Update(1,1,n,l,r,ma - temp);
Solve(u);
}
}
// 初始化
void Init()
{
for(int i = 0;i <= n; ++i) sum[i] = 1,in[i] = 0;
cal = 2,solve = 0;
}
int main()
{
scanf("%lld",&n);
Init();
for(int i = 1;i <= n; ++i) scanf("%d",&a[i]);
int x,y;
for(int i = 1;i < n; ++i)
{
scanf("%d %d",&x,&y);
v[x].push_back(y);
in[y]++;
}
for(int i = 1;i <= n; ++i)
{
if(in[i] == 0)
{
sta = i;
break;
}
}
DFS1(sta,a[sta]);
ma = a[sta],pos[sta] = 1;
DFS2(sta);
dfs[1] = a[sta];
Build(1,1,n);
Solve(sta);
printf("%lld\n",solve);
return 0;
}
官方的题解:分治的思想,如果几个点有共同的父亲,那么从它们到终端的时间就是相同的,把它们的时间都加到和它们中最大的一样即可,增加的时间加到 ans 中,从最底层开始算起,然后层层回溯。这个过程把原问题划分为了不同的子问题,一次 DFS 即可,比我的做法不知高到哪里去了 Orz。
附上本人代码:
#include
#include
#include
#include
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
ll n,ans = 0,sta;
ll a[maxn],in[maxn];
vector
ll Max(ll x,ll y)
{
return x > y ? x : y;
}
ll DFS(int x)
{
ll sum = 0,ma = -1;
ll len = v[x].size();
if(len == 0) return a[x];
for(int i = 0;i < len; ++i)
{
int u = v[x][i],temp;
temp = DFS(u);
ma = Max(ma,temp);
sum += temp;
}
ans += (ma * len - sum);
return ma + a[x];
}
int main()
{
while(scanf("%d",&n) != EOF)
{
ans = 0;
memset(in,0,sizeof(in));
for(int i = 1;i <= n; ++i) v[i].clear(),scanf("%lld",&a[i]);
int x,y;
for(int i = 1;i < n; ++i)
{
scanf("%d %d",&x,&y);
v[x].push_back(y);
in[y]++;
}
for(int i = 1;i <= n; ++i)
{
if(!in[i])
{
sta = i;
break;
}
}
DFS(sta);
printf("%lld\n",ans);
}
return 0;
}