Vj程序设计作业H14

A : 直径数量问题

题目描述

给出一棵树,求树的直径及其数量(最长简单路径的长度和数量)。

输入格式

第一行一个整数 n , 0≤n≤105,
接下来有 n−1 行,每行 a,b 代表 a 到第 b 有一条长度为 1 的边。
1≤a,b≤n

输出格式

输出两个数,分别表示树的直径和数量。

样例输入

5
5 1
1 2
2 3
2 4

样例输出

3 2

解答

#include<bits/stdc++.h>
#include<time.h>
#include <fstream>
#define forn(i,n) for(long long i=1;i<=n;i++)
const long long N = 1000100;
const long long mod = 1000000007;
using namespace std;
void in(long long &x){
    x=0;char c=getchar();
    long long y=1;
    while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    x*=y;
}
void o(long long x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)o(x/10);
    putchar(x%10+'0');
}
long long n;
vector<long long> g[N];
class pa{
public:
    long long len,cnt;
    void operator = (const pa &t){len=t.len,cnt=t.cnt;}
    bool operator < (const pa &t)const{return len*(long long)10000000+cnt<t.len*(long long)10000000+t.cnt;}
    pa(long long x,long long y):len(x),cnt(y){};
    pa(){len=1,cnt=1;}
};
pa dp1[N],dp2[N];
void dfs(long long u,long long fa){
    long long len1=0,len2=0;
    for(long long i=0;i<g[u].size();i++){
        long long v = g[u][i];if(v==fa)continue;

        dfs(v,u);

        if(dp1[u].len<dp1[v].len+1){
            dp1[u]=dp1[v];dp1[u].len++;
        }else if(dp1[u].len==dp1[v].len+1){dp1[u].cnt+=dp1[v].cnt;}

        if(dp1[v].len>len2)len2=dp1[v].len;
        if(len1<len2)swap(len1,len2);

        dp2[u]=max(dp2[u],dp1[u]);
        dp2[u]=max(dp2[u],dp2[v]);
    }
    if(len2==0)return;
    if(len1+len2+1>=dp2[u].len){
        if(len1+len2+1!=dp2[u].len)dp2[u].cnt=0;
        dp2[u].len=len1+len2+1;
        if(len1==len2){
            long long cnt = 0,sum = 0;
            for(long long i=0;i<g[u].size();i++){
                long long v= g[u][i];if(v==fa)continue;

                if(dp1[v].len==len1){
                    sum+=dp1[v].cnt*cnt;
                    cnt+=dp1[v].cnt;
                }
            }
            dp2[u].cnt+=sum;
        }else{
            long long cnt1=0,cnt2=0;
            for(long long i=0;i<g[u].size();i++){
                long long v= g[u][i];if(v==fa)continue;

                if(dp1[v].len==len1){
                    cnt1=dp1[v].cnt;
                }else if(dp1[v].len==len2){
                    cnt2+=dp1[v].cnt;
                }
            }
            dp2[u].cnt+=cnt1*cnt2;
        }
    }
}
signed main(){
    in(n);forn(i,n-1){long long x,y;in(x);in(y);g[x].push_back(y);g[y].push_back(x);}
    dfs(1,0);
    cout<<dp2[1].len-1<<" "<<dp2[1].cnt;
    return 0;
}

B : 城市规划

题目描述

有一座城市,城市中有 N 个公交站,公交站之间通过 N−1 条道路连接,每条道路有相应的长度。保证所有公交站两两之间能够通过唯一的通路互相达到。
两个公交站之间路径长度定义为两个公交站之间路径上所有边的边权和。
现在要对城市进行规划,将其中 M 个公交站定为“重要的”。
现在想从中选出 K 个节点,使得这 K 个公交站两两之间路径长度总和最小。输出路径长度总和即可(节点编号从 1 开始)。

输入格式

第 1 行包含三个正整数 N,M 和 K 分别表示树的节点数,重要的节点数,需要选出的节点数。
第 2 行包含 M 个正整数,表示 M 个重要的节点的节点编号。
接下来 N−1 行,每行包含三个正整数 a,b,c,表示编号为 a 的节点与编号为 b 的节点之间有一条权值为 c 的无向边。每行中相邻两个数之间用一个空格分隔。

输出格式

输出只有一行,包含一个整数表示路径长度总和的最小值。

样例输入

5 3 2
1 3 5
1 2 4
1 3 5
1 4 3
4 5 1

样例输出

4

解答

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int head[50010];
int tot;
bool bj[50010];
struct Edges{
    int u, v, nxt;
    int w;
} E[100010];
int n, m, K;
// ll f[11000][110];
vector<vector<ll>> f(5e4+10,vector<ll>(1e2+2,~(1u<<31u)));
int ct[500010];
void init(){
    tot =0;
    for (int i = 0; i <= n;i++){
        head[i] = -1;
        bj[i] = false;
        f[i][0] = 0;
    }
}

void addEdge(int u, int v,int w)
{
    E[tot].u = u;
    E[tot].w = w;
    E[tot].v = v;
    E[tot].nxt = head[u];
    head[u] = tot;
    tot++;
}

void dfs(int u,int fa){
    f[u][0] = 0;
    if(bj[u]){
        f[u][1] = 0;
        ct[u] = 1;
    }
    // if(bj[u])dp[u][1]=0,numbj[u]=1;
    for (int i = head[u]; i!=-1; i = E[i].nxt){
        if(E[i].v==fa){
            continue;
        }
        dfs(E[i].v, u);
        ct[u] += ct[E[i].v];
        for (int j = min(K, ct[u]); j >= 1;j--){
            for (int l = 1; l <= j and l <= min(K, ct[E[i].v]);l++){
                f[u][j] = min(f[u][j], f[E[i].v][l] + f[u][j - l] + 1ll * l * (K - l) * E[i].w);
            }
        }
    }
}
int main(){
    cin >> n >> m >> K;
    init();
    for (int i = 1; i<=m;i++){
        int x;
        cin >> x;
        bj[x] = true; // 标记重要节点
    }
    for (int i = 1; i <= n - 1;i++){
        int x, y;
        int z;
        cin >> x >> y >> z;
        addEdge(x, y, z);
        addEdge(y, x, z);
    }
    dfs(1, -1);
    cout << f[1][K] << "\n";
    return 0;
}

C : 最大区间和

题目描述

输入一个长度为 n 的整数序列 a,从中找出一段不超过 m 的连续子序列(区间),使得这个序列的和最大。选出的区间可以为空。
n≤106,m≤n,−109≤ai​≤109

输入描述

第一行两个数 n,m,第二行 n 个整数 ai​ 表示这个数列。

输出描述

一个整数表示答案。

样例输入

6 3
1 -3 5 1 -2 3

样例输出

6

解答

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[1000010], b[1000010];
ll n, m;
int main(){
    cin >> n >> m;
    for (int i = 1; i <= n;i++){
        cin >> a[i];
        if(i == 1){
            b[i] = a[i];
        }else{
            b[i] = a[i] + b[i - 1];
        }
    }
    ll ans = 0;
    // vector<ll> q;
    deque<ll> q;
    q.push_back(0);
    for (int i = 1; i <= n;i++){
        while(!q.empty() and b[q.back()]>b[i]){
            q.pop_back();
        }
        q.push_back(i);
        while(!q.empty() and i-m>q.front()){
            q.pop_front();
        }
        ans = max(ans, b[i] - b[q.front()]);
    }
    cout << ans << "\n";
    return 0;
}

 

D : 帝国大厦

题目描述

帝国大厦共有 n 层,LZH 初始时在第 a 层上。
帝国大厦有一个秘密实验室,在第 b 层,这个实验室非常特别,对 LZH 具有约束作用,即若 LZH 当前处于 x 层,当他下一步想到达 y 层时,必须满足 ∣x−y∣<∣x−b∣,而且由于实验室是不对外开放的,电梯无法停留在第 b 层。
LZH 想做一次旅行,即他想按 k 次电梯,他想知道不同的旅行方案个数有多少个。
两个旅行方案不同当前仅当存在某一次按下电梯后停留的楼层不同。

输入格式

一行 4 个数 n,a,b,k。

输出格式

一个数表示答案,由于答案较大,将答案对 109+7 取模后输出。

数据范围与子任务

对于 40% 的数据 n,k≤500。
对于 80% 的数据 n,k≤2000。
对于 100% 的数据 n,k≤5000,1≤a,b≤n,a​=b。

测试样例

样例 1

输入:

5 2 4 1

输出:

2

样例 2

输入:

5 2 4 2

输出:

2

样例 3

输入:

5 3 4 1

输出:

0

解答

#include<bits/stdc++.h>
using namespace std;
const long long N = 5010;
const long long mod = 1000000007;

long long n,d,b,k;
vector<long long> sum(N);//前缀和
long long a[2][N];
int main(){
    cin >> n >> d >> b >> k;
    a[k&1][d]=1;
    for(long long i=d;i<=n;i++)sum[i]=1;
    for (int j = k-1; j >= 0; j--)
    {
        for(long long i=1;i<=n;i++){
            if(i>b){
                long long l = (i+b)/2;
                a[j&1][i]=sum[n]-sum[l];
            }else if(i<b){
                long long r = (i+b-1)/2+1;
                a[j&1][i]=sum[r-1]-sum[0];
            }
            a[j&1][i]-=a[(j+1)&1][i];
            a[j&1][i]+=mod;
            a[j&1][i]%=mod;
        }
        // 前缀和
        for(long long i=0;i<=n;i++){
            sum[i]=a[j&1][i];
            sum[i]+=sum[i-1];
            sum[i]%=mod;
        }
    }
    long long ans = 0;
    for(long long i=1;i<=n;i++){
        ans += a[0][i];
        ans%=mod;
    }
    cout << ans << "\n";
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值