[Offer收割]编程练习赛24

HihoCoder 1562 ⼩Hi的钟表

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

⼩Hi喜欢各种⾓度。⼀天,他注意到了钟表上的⾓度,于是他想考考他的好朋友⼩Ho:对于⼀个24⼩时制的时刻,在 t 秒之后,对应在钟表上时针与分针的夹⾓是多少。为保证答案的唯⼀性,只需考虑不超过180°的⾓。你能帮助⼩Ho解决这个问题吗?  

例如,下图可表⽰15点30分0秒经过0秒后的时间,其对应的夹⾓为75°和285°,在这个问题中我们只考虑不超过180°的⾓,所以此时的夹⾓为75°。


输入

输⼊包含多组测试数据。

第⼀⾏为测试数据的组数T(1 ≤ T ≤ 1000)  

对于每组数据:  

第⼀⾏包含三个整数hms,表⽰给定时刻的时、分、秒(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<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
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公司的终端

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

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行每行包含两个整数 ab,表示 ab 的父节点。

对于30%的数据,满足 1 ≤ N ≤ 103

对于100%的数据,满足1 ≤ N ≤ 105, 1 ≤ Ai ≤ 105

输出

输出一个整数表示增加的处理时间之和最小是多少。

样例输入
4  
1 1 1 1  
1 2  
1 3  
3 4
样例输出
1
这道题就有点迷了。。。当时一看就想出了一种比较。。。复杂的做法,也能是最近刚接触了这些概念吧,但是迷之 WA 了两天。。。直到今天忍不住看了官方题解正解是分治的思想,于是很快又写出了一份代码,然后又是迷之 WA 了 n 次。一怒之下把 int 都改成了 long long ,然后。。。就过了微笑。说好的Ai <= 1e5 呢?

自己的想法:

增加时间的节点应尽量靠上。

预处理:我们可以把整棵树的 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<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#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> v[maxn];
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<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
ll n,ans = 0,sta;
ll a[maxn],in[maxn];
vector<ll> v[maxn];
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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值