人之所以能,是相信能 jzoj 2017.8.19 B组

第一题

  1. 经济编码 (Standard IO)
    Time Limits: 1000 ms Memory Limits: 128000 KB Detailed Limits
    Goto ProblemSet

Description

  为降低资料储存的空间或增加资料传送的速度,编码是常用的方法。
  假设有一个字符集,每个字符出现的频率是已知的。现在要把每个字符编码成为一个二元字串(例如把“A”编码作101),采用的编码必须合乎以下条件:一个字符的编码不可以是另一个字符的前置(prefix)。前置的定义如下:若一个字串S1为另一个字串S2的前置,则从S2的最后一个字符开始,连续删除一定数量的字符后可以得到S1(S2本身也是S2的前置),举例而言:如果字符“A”的编码是101,而字符“B”的编码为01,则“B”的编码不为“A”编码的前置;如果字符“C”的编码为1100,而字符“D”的是11,则“D”的编码是“C”编码的前置。以下的编码方式可以在符合这个条件下给出最经济的编码,请找出使用下述方法做最经济编码时,一个字符编码的预期长度。
  这里写图片描述
  这里写图片描述
  编码法:
  1、 如以下所述建立一棵二元树。
  先从字符集选取两个出现频率最低的字符作合并,合并后以一个全新的虚拟字符取代这两个字符,新字符的频率等于这两个旧字符频率的总和,并令这两个旧字符为此新字符的两个子树,左右不限。重复以上操作,直至字符集剩下一个字符为止。如下图(i)到(iv)。
  2、 再依照以下所述方法将各字符作编码。
  由上一步骤所得之二元树,将每个内部节点(internal node)连往左子树的边(edge)标记为“0”,连往右子树的边标记为“1”,如下图(V)所示。一字元的编码即为从树根(root)至此字符,经过的每一个边的标记所成之字串。在此“a”编码作000,“?”编码作01。
  在按照上述的编码法完成最经济编码之后,就可以计算这个字符编码的预期长度。首先算出每个字符的预期长度=编码长度×出现频率,然后把所有字符的预期长度结合起来,就可以得到此字符编码的预期长度。下表是上述编码的计算范例。

字符 编码 编码长度 出现频率 预期长度
a 000 3 0.1 0.3
b 001 3 0.1 0.3
? 01 2 0.3 0.6
8 1 1 0.5 0.5

    字符编码的预期长度 1.7

Input

  第一行为两整数n和m,分别代表字符集的大小和文章总长度。然后每一个字符分行列出,每行列出一字符出现的次数。

Output

  预期一个字符编码的长度,保留至小数点后6位。

Sample Input

输入范例1:
4 10
1
1
3
5

输出范例1:
1.700000

输入范例2:
6 100
30
30
5
25
5
5

输出范例2:
2.250000

Sample Output

Data Constraint

Hint

【数据约定】
  对于50%的数据,1<=n<=2000;
  对于100%的数据,1<=n<=200000。
  所有字符出现次数和等于文章总长度。
【提示】
  建议使用extended类型处理实数运算。

题解:明显的哈夫曼树,那个编码就是哈夫曼编码,求WPL嘛,每个点点劝加在一起就好啦。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
int n,sum=0;
double m,ans=0;
struct hh
{

    double pl;
    double cs;
    double cd;
    int pos;    
    friend bool operator < (hh n1,hh n2)//重载小于号
      {
          return n1.pl > n2.pl;
     }
}a[200010],t[500000][2],x,y;
int tt=1;
priority_queue<hh>q;
hh minn(hh a,hh b)
{
    if(a.pl<=b.pl) return a;
    else return b;
} 
hh maxx(hh a,hh b)
{
    if(a.pl>b.pl) return a;
    else return b;
} 
int main()
{   
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].cs;
        a[i].pl=a[i].cs;        
        a[i].pos=i;
        q.push(a[i]);

    }
    while(1)
    {
        x=q.top();
        q.pop();
        y=q.top();
        q.pop();
        if(x.pl==y.pl)
        {
            t[tt][1]=x;
            t[tt][2]=y;
        }
        else 
        {
            t[tt][1]=minn(x,y);
            t[tt][2]=maxx(x,y);
        }
        hh c;
        c.pl=x.pl+y.pl;
        c.pos=-1;
        q.push(c);
        tt++;
        if(q.size()==1) break;

    }

    int j=1;
    for(int i=tt;i>=1;i--)
    {
        ans+=t[i][1].pl+t[i][2].pl;
    }
    printf("%.6lf",ans/m);
    return 0;
}

第二题

  1. 【雅礼联考GDOI2017模拟9.2】Ztxz16学图论 (Standard IO)
    Time Limits: 3000 ms Memory Limits: 262144 KB Detailed Limits
    Goto ProblemSet

Description

众所周知,Zjr506是算法之神,因此Ztxz16经常向他请教算法。这一天,Zjr506在教导了Ztxz16关于图论方面的一些算法后,给他出了一道图论题作为家庭作业:
给定N个点,M条无向边,Q个询问,每个询问给定L, R,问连上第L~R条边后,图中有多少联通块(询问之间互不影响)。
Ztxz16智商太低,百思不得其解,只好向你请教这个问题。

Input

第一行输入N M Q
接下来M行每行两个整数代表一条边
接下来Q行每行两个整数代表一个询问

Output

输出Q行,代表这个询问中联通块的个数

Sample Input

3 3 3
1 2
2 3
1 3
1 1
2 3
1 3

Sample Output

2
1
1

Data Constraint

20%的数据保证N, M, Q <= 1000
60%的数据保证N, M, Q <= 50000
100%的数据保证N, M, Q <= 200000, L <= R

题解:30分:直接并查集

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long 
using namespace std;
LL ans;
struct hh
{
    int from,to,date;
}a[22000];
bool pd[22000];
int n,m,q,num=0,fa[22000];
void add(int from,int to)
{
    num++;
    a[num].from=from;
    a[num].to=to;
}
int check (int i)
{
    if(fa[i]==i)
    return i;
    fa[i]=check(fa[i]);
    return fa[i];
}
void update(int l,int r)
{
    for(int i=l;i<=r;i++)
    {int x=check(fa[a[i].from]),y=check(fa[a[i].to]);
    fa[y]=x;
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    cin>>n>>m>>q;
    for(int i=1;i<=m;i++)
    {
        int f,t;
        cin>>f>>t;
        add(f,t);
    }
    for(int i=1;i<=q;i++)
    {
        ans=0;
        memset(pd,false,sizeof(pd));
        for(int j=1;j<=n;j++)
        fa[j]=j;
        int l,r;
        cin>>l>>r;
        update(l,r);
        for(int j=1;j<=n;j++)
        {
            fa[j]=check(fa[j]);
            if(pd[fa[j]]==false) ans++;
            pd[fa[j]]=true;
        }
        cout<<ans<<endl;
    }
    return 0;
}

AC题解:(目前没写出,先贴官方题解)
对于左端点为 i 的所有询问,我们先考虑从 i 号边开始往后做并查集,树边标记为 1,
环边标记为 0,这样只需维护区间和即可处理询问。
考虑左端点左移一步,既从 i 变成 i - 1, 从这条边开始做并查集。因为第 i - 1 是最
初加进来的边,所以肯定是树边,并且有可能导致之后的某一条边变成了环边。很显然,这条
边是第 i - 1 条边的两个端点路径上标号最大的边, 我们可以用 link-cut-tree 找出这条边并
删去
这样,只需将询问按左端点从大到小标号,然后用 link-cut-tree 维护并查集即可完成
所有询问。
当然也可以用莫队算法。

第三题

  1. 【雅礼联考GDOI2017模拟9.2】Zjr506的捕猫计划 (Standard IO)
    Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits
    Goto ProblemSet

Description

Zjr506很喜欢猫,某一天他突然心血来潮,想捕捉学校里活动的猫。
为了捕猫,Zjr506在校园中放置了N个木桩,当他见到有猫进入他的狩猎范围后,就会以迅雷不及掩耳的速度在一些木桩之间绕上藩篱以困住这些猫。
一段时间后,Zjr506在绕了M个藩篱后兴高采烈的离开了。作为正义的使者,Ztxz16不忍心看到这些猫受到折磨,于是决定拆除一些藩篱让所有的猫都逃出去。因为Zjr506的巧妙设计,藩篱不会在除木桩之外的地方相交。这些藩篱构成了一些封闭的区域,每一个区域中都有一只猫。
因为Zjr506制造这些藩篱也不容易,所以Ztxz16希望拆除的藩篱总长度尽量小,现在他希望你告诉他最小的总长度。

Input

第一行两个数 n,m ( 2<=n<=10000 , 1<=m<=50000 )
接下来 n 行 , 每行两个整数 xi,yi, 代表第 i 个木桩的坐标 ( − 10000 ≤ xi, yi ≤ 10000).
接下来 m 行,每行两个整数 pi,qi (1 ≤ pj, qj ≤ N) , 代表木桩 pi 与木桩 qi 之间有一个藩篱。

Output

输出一个实数代表答案,当答案与标准答案差的绝对值不超过0.001时认为它是正确的。

Sample Input

8 8
0 0
3 0
3 3
0 3
1 1
1 2
2 2
2 1
1 2
2 3
3 4
4 1
5 6
6 7
7 8
8 5

Sample Output

4.000000000

Data Constraint

对于20%的数据:
N , M <= 30
对于30%的数据:
N, M <= 300
对于100%的数据:
N <= 10000, M <= 50000

题解:直接跑一次最大生成树,原图总边权减去最大生成树的总边权就好啦。(10分钟码完)

#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
int zb[20000][3],k=0,fa[20000];
double sum=0,h=0;
struct hh
{
    int from,to;
    double date;
}a[60000];
int n,m,num=0;
void add(int f,int t)
{
    num++;
    a[num].from=f;
    a[num].to=t;
    a[num].date=sqrt(pow(zb[f][1]-zb[t][1],2)+pow(zb[f][2]-zb[t][2],2));
    h+=a[num].date;
}
bool come(hh a,hh b)
{
    return a.date>b.date;
}
int find(int i)
{
    if(fa[i]==i)
    return i;
    fa[i]=find(fa[i]);
    return fa[i];
}
int unionn(int x,int y)
{
    int fal=find(x);
    int fbl=find(y);
    fa[fbl]=fal;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    fa[i]=i;
    for(int i=1;i<=n;i++)
    {
        cin>>zb[i][1]>>zb[i][2];
    }
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        add(x,y);
    }
    sort(a+1,a+num+1,come);
    for(int i=1;i<=num;i++)
    {
        if(find(a[i].from)!=find(a[i].to))
        {
            unionn(a[i].from,a[i].to);
            sum+=a[i].date;
            k++;
        }
        if(k==n-1)
        break;
    }
    printf("%.9lf",h-sum);
    return 0;
}

总结:被第一题坑了,不知道哈夫曼树是真的惨,搞了两个小时。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值