当前位置: 移动技术网 > IT编程>开发语言>C/C++ > 泉五培训Day1

泉五培训Day1

2019年01月29日  | 移动技术网IT编程  | 我要评论

河南专升本,混沌都市逍遥神,刷砖平台

t1 树学

题目

【问题描述】

给定一颗 n 个点的树,树边带权,试求一个排列 p,最大化下式

其中,calc(a, b)表示树上由a到b经过的最大边权。

【输入格式】

第一行一个整数 n,表示点数下接 n − 1 行,每行三个数 u, v, w 表示一条连接点 u 和点 v 权值为 w

【输出格式】

总共一行一个整数,表示答案

【输入样例】

2

1 2 2333

【输出样例】

2333

【数据范围】

对于前5%的数据满足 n ≤ 8

对于前40%的数据满足 n ≤ 20

对于前60%的数据满足 n ≤ 200

对于100%的数据满足 n ≤ 100

解析

先从边权最小的边开始累加,尽可能的减少当前边的贡献次数。发现每轮最少贡献一次,即将边两侧的点在p中完全分开。

试过多组数据之后,不难发现,答案即为所有边权之和。

1、最长边只计算过一次。

2、如果次长边被经过两次及以上,从次长边的一端出发,到了最长边的一端,走了一个来回,很显然,这是错误的,所以,次长边只计算过一次。

3、以此类推,最后,所有边都只计算过一次。

code

#include <iostream>
#include <cstdio>
using namespace std;
int n,u,v,w;
long long ans;
int main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        cin>>u>>v>>w;
        ans+=w;
    }
    cout<<ans;
    return 0;
}
view code

 

 

 

 

 

t2 保留道路

题目

【问题描述】

很久很久以前有一个国家,这个国家有n个城市,城市由1,2,3,…,n标号,城市间有m条双向道路,每条道路都有两个属性g和s,两个城市间可能有多条道路,并且可能存在将某一城市与其自身连接起来的道路。后来由于战争的原因,国王不得不下令减小花费从而关闭一些道路,但是必须要保证任意两个城市相互可达。

道路花费的计算公式为wg*max{所有剩下道路的属性g}+ws*max{所有剩下道路的属性s},其中wg和ws是给定的值。国王想要在满足连通性的前提下使这个花费最小,现在需要你计算出这个花费。

【输入格式】

输入文件名为road.in。

第一行包含两个正整数n和m。

第二行包含两个正整数wg和ws。

后面的m行每行描述一条道路,包含四个正整数u,v,g,s,分别表示道路连接的两个城市以及道路的两个属性。

【输出格式】

输出文件名为road.out。

输出一个整数,表示最小花费。若无论如何不能满足连通性,输出-1。

【输入输出样例】

road.in

road.out

3 3

2 1

1 2 10 15

1 2 4 20

1 3 5 1

 

30

 

【数据规模与约定】

对于10%的数据,n≤10,m≤20;

对于30%的数据,n≤100,m≤1000;

对于50%的数据,n≤200,m≤5000;

对于100%的数据,n≤400,m≤50000,wg,ws,g,s≤1000000000。

解析

这题难在最小生成树有两个变量。

先按g从小到大排序,在加入边的过程中发现,当前的最小生成树只会由上一次的最小生成树和新边构成。

所以每次只需在n条边中生成最小生成树即可。

code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 100000000000000000ll;
#define m 50010
#define n 410
long long ans=inf;
long long n,m,ws,wg,cnt,f[n];//cnt表示已生成的树的边数 
struct rec{
    long long x,y;
    bool vis;//是否被使用过 
    long long g,s;
}tree[m],small[m],ra[m];//tree表示初始数据,small表示最小生成树的数据,ra是最小生成树的替身 
int find(long long x)
{
    if(f[x]==x) return x;
    return f[x]=find(f[x]); 
}
bool cmp(rec a,rec b)
{
    if(a.g!=b.g) return a.g<b.g;
    else return a.s<b.s;
}
void kruskal(long long maxg)//生成最小生成树 
{
    for(int i=1;i<=cnt;i++)
    {
        ra[i]=small[i];//复制数据 
        ra[i].vis=0;//初始化,0为未访问 ,1为访问 
    }
    long long maxs=0,tot=0;
    for(int i=1;i<=n;i++)   f[i]=i; //并查集初始化
    for(int i=1;i<=cnt;i++)
    {
        int a=find(small[i].x);
        int b=find(small[i].y);
        if(a!=b)
        {
            maxs=max(maxs,small[i].s);
            f[a]=b;
            tot++;
            ra[i].vis=1;//标记 
        }
        if(tot==n-1) 
        {
            int num=0;
            for(int i=1;i<=cnt;i++)
                if(ra[i].vis) small[++num]=ra[i]; 
            cnt=num;
            ans=min(ans,maxg+maxs);
            break; 
        }
    }
}
void start()
{
    for(int i=1;i<=m;i++)//按g的大小,从小到大枚举边
    {
        if(tree[i].g+tree[i].s>ans) continue;
        int pos=cnt+1;
        for(int j=1;j<=cnt;j++)
        {
            if(small[j].s>tree[i].s)
            {
                pos=j;
                break;
            }
        }
        if(pos==cnt+1)
        {
            cnt++;
            small[cnt]=tree[i];
        }
        else
        {
           cnt++;
           for(int j=cnt;j>=pos+1;j--) small[j]=small[j-1];
           small[pos]=tree[i];
        }
        kruskal(tree[i].g);
     } 
}
int main()
{
    cin>>n>>m>>wg>>ws;
    for(int i=1;i<=m;i++)
    {
        cin>>tree[i].x>>tree[i].y>>tree[i].g>>tree[i].s;
        tree[i].g*=1ll*wg;
        tree[i].s*=1ll*ws;//初始化 
    } 
    sort(tree+1,tree+m+1,cmp);//将原数据的g从小到大排序
    start();
    cout<<ans;
    return 0;
}
view code

 

 

 

 

 

t3 appointment

题目

【问题描述】

定义两个排列v_i和g_i的“友好值”为:在1 <= i <= n中,出现v_i > g_i的次数。

给定n,m,以及一个1~n的排列v_i,你需要分别求出和排列v_i“友好值”为0,1,2,...,m的排列g_i有多少个。

【输入格式】

输入一共有两行。

第一行包含两个整数 n,m,意义如上

第二行包含 n 个整数,第 i 个为 vi,意义如上

【输出格式】

输出共 m+1 行,第i行友好值为 i-1 时的g_i有多少个。由于答案可能较大,请输出ans%998244353 的结果。

【样例输入】

3 2

1 3 2

【样例输出】

1

4

1

【样例解释】

友好值为0时,排列g只能是(1, 3, 2) 

友好值为1时,排列g可以是(1, 2, 3)  (2, 1, 3)  (2, 3, 1)  (3, 1, 2)    

友好值为2时,排列g只能是(3, 2, 1)

【数据范围】

对于20%的数据: n <= 10

对于100%的数据: n <= 6000

解析

先试几组数据,可以发现答案与v_i无关。

设f[i][j]表示当前序列长度为i时,友好值为j。

如果在原有序列中加入一个新数字,原有序列的友好值要么+1要么不变。

若友好值不变,则把新加入的数和一个具有贡献的数交换位置,共有(j+1)种换法。

若友好值+1,则把新加入的数和一个没有贡献的数交换位置,即(i-1)-(j-1),即i-j。

于是得出了状态转移方程f[i][j]=f[i-1][j]*(j+1)+f[i-1][j-1]*(i-j)。

边界:f[i][0]=1。

code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long mod=998244353;
long long f[10000][10000];
int main()
{
    int n,m,temp;
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>temp;
    for(int i=1;i<=n;i++) f[i][0]=1;
    for(int j=1;j<=m;j++)
        for(int i=1;i<=n;i++) f[i][j]=(f[i-1][j]*(j+1)%mod+f[i-1][j-1]*(i-j)%mod)%mod;
    for(int i=0;i<=m;i++) cout<<f[n][i]<<endl;
    return 0;
 } 
view code

 

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网