2019.10.26 CSP%您赛第三场

Posted qxds

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019.10.26 CSP%您赛第三场相关的知识,希望对你有一定的参考价值。

(CSP)凉心模拟^_^

——题源(lqx、lhc)等各位蒟蒻

题目名称 比赛 传递消息 开关灯
源文件名 (competition.cpp) (message.cpp) (light.cpp)
输入文件名 (competition.in) (message.in) (light.in)
输出文件名 (competition.out) (message.out) (light.out)
测试点时限 (1s) (1s) (2s)
内存限制 (256MB) (256MB) (256MB)
题目分值 (100) (100) (100)
测试点个数 (10) (10) (10)
测试点分值 (10) (10) (10)
题目类型 传统 传统 传统

说明:

(1).代码长度限制为 (10KB),编译时开启 (-O2) 开关。

(2.)请将自己的代码按照要求放入文件夹内。

(3.)请注意不要因为非技术因素导致程序无法正常通过数据,其中你要注意到的包括但不限于:

((1).)内存使用情况。

((2).)是否使用文件输入输出,文件输入输出的(.in/.out) 的文件名是否正确,源程序的文件名是否正确。源程序的文件名和(.in/.out) 的文件名是否有不可见字符,如果有,则认为文件名错误,不能得分。

((3).)保存文件的路径是否正确。

((4).)是否删除调试信息。

((5).)是否能通过所有样例。

((6).)输出格式是否正确。

((7).)变量类型是否正确。

((8).)考试结束后请不要在考场周围疾跑、翻滚、受身、空跃、吼叫、捉云手、豪龙破军、千斤坠、星落、乱射、仙人指路、换位、瞬间移动、乱雷、清扫、重力加速拍、讨论。

((9).)请注意没有样例解释。自己弄去。(暴躁蒟蒻在线坑人)

((10).)引言&题目背景

我们的故事开始在一个晴朗的下午。荣耀联盟(注:某电竞组织)的当家选手们站在颁奖台上合照,庆祝新赛季的结束。

“哎我说苏妹子你往左一点挡到我了本剑圣场场比赛千万上下的脸怎么能被你挡住那边点把四亚挤一边去……”有个声音在后面喋喋不休,我们只能看到一个黄头发的脑袋——职业选手黄少天。

“行了,少天。安静点。”一个温和的声音。“回去加训。站哪不都一样。”

“唔。苏队。这里。”周泽楷在招手叫人过去。

“行了,沐橙。跟着哥有肉吃。“叶修搂过小姑娘的肩膀,很欠揍地笑笑。

远处的姑娘在镁光灯下笑得灿烂,这是我们的第一个主角,也就是刚才说的苏妹子——苏沐橙。她不说话,只是看着他们——我们剩下的主角——刚才提到的他们。“无论世界如何,”她想。“至少还有你们。不同的你们。”

却是一样的荣耀。


当你们做到这场比赛,可能上面这些的作者已经退役。但是无论如何,无论你们今后要在这条路上走多远,无论会何时停步,都请你回头看看来时的路,那里有你的——我们的——荣耀。

题目背景源《全职高手》。

以上。


比赛

题目背景

众所周知,苏沐橙是我们的联盟女神。现在我们站在全明星赛的团队赛场上。双方选手被分成了两组,只要有一方获胜,他们就更有可能获得苏沐橙的倾心——更重要的是,冠军。

问题描述

双方采用一局定胜负的形式。双方选手每人有一个能力值,当双方派上场的选手能力值一方大于另一方时,能力值大的一方获胜,若两方能力值相等则平局。但不幸的是,任意一队上场选手抽签决定,每队每名选手上场概率相等。

苏沐橙饶有兴趣地看着场上的比赛。她想知道叶修带领的一方获胜的概率。

输入格式

第一行一个正整数(T),表示数据组数。((1leq Tleq 5))

接下来(T)组数据,第一行两个数(N)(M),分别表示叶修带领一方人数和周泽楷一方人数。

第二行(N)个正整数(a[i]),表示叶修一方选手能力值。

第三行(M)个正整数(b[i]),表示周泽楷一方选手能力值。

输出格式

对于每组数据输出仅一行形如(x/y),表示叶修一方获胜概率。要求输出最简分数。

样例

输入样例

(competition.in)

1
3 5
4 3 7
1 9 4 6 10

输出样例

(competition.out)

1/3

数据范围与提示

具体子任务限制及得分情况如下表:

限制 分数占比
叶修/周泽楷必赢(不给人点机会吗) (0\%)
(1leq n,mleq 2000) (50\%)
(1leq n,mleq 40000;1leq a[i],b[i]leq 10^9) (100\%)

传递消息

题目背景

众所周知,荣耀联盟的职业选手们拥有自己的选手宿舍。为了方便大家联系同时最大限度减少经费,每两间宿舍之间有且只有一条简单道路。黄少天和他的沐沐打算先到场中央,但是不幸的是,他们住的宿舍是所有宿舍当中最远的两间。

问题描述

现在你是联盟主席,为了促成这两人的好事(什),你希望他们相遇的时间尽可能短。但因为道路拥挤,所有道路的拥挤度总和必须大于等于一个值。你现在需要安排道路的拥挤程度,使得黄少天与沐沐之间的道路的拥挤度最小。

一句话题意:现在给你一棵树,其所有边的初始边权为(0)。给你一个值(S)代表新加入的边权总和,要求你将总和为(S)的边权分给一些边(每边得到的边权可以为分数),要求得到的新树直径最小。

注:树的直径:树上最长的链。

输入格式

输入第一行包括两个数(n)(S),其中(n)表示点的数目,(S)表示边权总量。

接下来(n-1)行每行包括两个数(a)(b),表示(a)(b)间有一条边,保证输入的(a,b)合法且不重复。

输出格式

输出仅一行,包括一个浮点数表示树的直径。这个浮点数要求精确到小数点后(4)位。

样例

输入样例

(message.in)

4 3
1 2
1 3
1 4

输出样例

(message.out)

2.0000

数据范围与提示

具体子任务限制及得分情况如下表:

子任务 限制 分数
(subtask1) (forall b[i]=a[i]+1) (30\%)
(subtask2) (forall a[i])相等 (30\%)
(subtask3) (2leq nleq 10^5,2leq sleq 10^9) (100\%)

开关灯

题目背景

经过一天的忙碌,叶修和他的沐沐总算回到了自己家。他们现在打算关灯休息。(咳……不是你们想的那个)

问题描述

叶修家里一共有很多盏灯,它们构成一个(n imes m)的矩阵,它们均由(0/1)构成。其中(0)表示灯灭,(1)表示亮。我们定义一下两种操作:

((1))将某一列(0)(1)的状态反转(即每盏灯的开关按一下)

((2))将某两行的状态交换。

只有第一个操作每次记作一步。现在给定灯的初始状态,求从这个初始状态经过最少几步可以得到结束状态。如果不能在有限步内得到结果,输出(Impossible)

输入格式

第一行有一个整数(T),表示有多少组测试数据。

每组测试数据包含三行。第一行为两个整数(n, m)

每组数据的第二行为(n)个长度为(m)(0/1)字符串,依次描述起初每行的灯的开关状态。第(i)个字符串的第(j)个字符若是(1),表示对应位置的灯是亮的;(0)表示是灭的。

每组数据的第三行为(n)个长度为(m)(0/1)字符串,描述希望达到的所有灯的开关状态。格式同上。

输出格式

输出(T)行,依次为每组测试数据的答案。如果不可能达到,输出(Impossible);否则输出最少按多少次开关。

样例

输入样例

(light.in)

3
3 2
01 11 10
11 00 10
2 3
101 111
010 001
2 2
01 10
10 01

输出样例

(light.out)

1
Impossible
0

数据范围与提示

具体子任务限制及得分情况如下表:

限制 分数占比
注意。没有部分分。 (0\%)
(nleq 10^3;mleq 50) (100\%)

(Prob1) 比赛

算法1

在第一部分数据中,有(1leq n,mleq 2000),所以暴力枚举每组可能的出场,统计叶修(/)周泽楷队的输赢即可。

时间复杂度(O(nm))。期望得分(50pts)

算法2

考虑优化掉一部分状态。我们容易知道,当叶修(/)周泽楷一方派出选手(A),另一方派出选手(B)且选手(A)可以赢时,所有能力值比(A)高的选手都可以赢(B)

所以考虑将两个给定的序列从小到大排序。对于(A)队每个人,找到第一个(B)队中比(A)能力值高的选手,则不需考虑(B)队中其余选手与(A)的情况。复杂度(O(nlogn))。期望得分(100pts)
(std:)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=40001;
int T,n,m,a[MAXN],b[MAXN];
int gcd(int a,int b){if(!b) return a;return gcd(b,a%b);}
void solve(){
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    sort(a+1,a+n+1);
    for(int i=1;i<=m;i++) b[i]=read();
    sort(b+1,b+m+1);
    int l=0,fz=0;
    for(int i=1;i<=n;i++){
        while(l<m&&b[l+1]<a[i]) l++;
        fz+=l;
    }
    int fm=n*m;
    int Gcd=gcd(fz,fm);fz/=Gcd,fm/=Gcd;
    printf("%lld/%lld
",fz,fm);return;
}
signed main(){
    freopen("competition.in","r",stdin);
    freopen("competition.out","w",stdout);
    T=read();
    while(T--) solve();
}

(Prob2) 传递消息

算法1

考虑第一个部分分,即(forall b[i]=a[i]+1),容易知道原树是一条链。所以无论如何分配(S)的边权总量,树的直径一定是(S),则输出(S)即可。

时间复杂度(O(1))。期望得分(30pts)

算法2

考虑第二个部分分,即(forall a[i]=1),则原图是一个菊花图(注:菊花图:只有两层节点的树)。则容易知道所有可能的最长链一定经过唯一的一个(a[i]),则考虑将(S)平均分配给每条链再乘以2即可。

时间复杂度(O(1))。期望得分(30pts),加上算法一共(60pts)

算法3

对于一整棵树,容易知道在无负权边的情况下,树的直径的两个端点一定都是叶子节点。而容易知道,与每个叶子节点直接相邻的边对答案所做的贡献在所有边中一定是最少的(容易明白给靠近叶子节点的边一个较大的权一定比给中间某一条边一个较大的权划算)。

(proof:)

容易知道,因为整棵树的边权都是我们自己分配的,所以对于所有获得边权的边,容易证明它们获得的所有边权相等;

设我们这棵树一共有(l)个叶子节点,则若赋给所有与叶子节点相邻的边边权,每条边获得的边权是
(frac{S}{l})。则易知树的直径是(frac{2S}{l})

当我们增加一条获得边权的边,则每条边获得的边权为(frac{S}{l+1}),则因为每个叶子节点对应着一条有边权的边,则树的直径必定经过三条有边权的边,则树的直径为(frac{3S}{l+1}),可知当(lgeq 2)(frac{2S}{l}geq frac{3S}{l+1}),而因为该树至少需要有(3)层节点(用以保证树的直径为两个叶子节点之间的距离),所以第一种构造方法是最优的构造方法,故上述构造方法成立。

(:)对于(l)个叶子节点,分给每条与叶子节点相邻的边(frac{S}{l})边权,则树的直径为(frac{2S}{l})

时间复杂度(O(1))。期望得分(100pts)
(std:)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=500001;
int n,k,du[MAXN],ans;
int main(){
    freopen("message.in","r",stdin);
    freopen("message.out","w",stdout);
    n=read(),k=read();
    for(int i=1;i<n;i++)du[read()]++,du[read()]++;
    for(int i=1;i<=n;i++)
        if(du[i]==1) ans++;
    printf("%.4lf",2*1.0*k/ans);
}

(Prob3) 开关灯

根据题意,我们知道,因为列上每个数的变换是统一的,所以所有行上所有位置的数的变化次数相同。

考虑如何计算出这个变化次数。因为涉及到行之间的交换,我们枚举第一行经过数次交换后交换到了新的(01)矩阵的第几行,从而可以推出每列的开关灯次数。根据刚才推出的每列次数进行模拟,如果模拟后每行状态构成的集合与答案矩阵每行的状态构成的集合等价(因为行之间可以随意交换),即可通过列的变换次数得到答案。

枚举每一行需要的复杂度为(O(n))。检查答案需要的复杂度为(O(n))。总复杂度(O({n^2}))。期望得分(100pts)
(std:)

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<ctime>
#include<climits>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=160,L=60;
char ch[N][L];
ll n,l,a[N],b[N],tmp,c[N]; 
int main()
{
    freopen("light.in","r",stdin);
    freopen("light.out","w",stdout);
    ll i,j,k,T,ans,cnt;
    scanf("%lld",&T);
    while(T--)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        scanf("%lld%lld",&n,&l);
        ans=l+1;
        for(i=1;i<=n;i++) scanf("%s",ch[i]+1);
        for(i=1;i<=n;i++) for(j=1;j<=l;j++) a[i]=a[i]*2+ch[i][j]-'0';
        for(i=1;i<=n;i++) scanf("%s",ch[i]+1);
        for(i=1;i<=n;i++) for(j=1;j<=l;j++) b[i]=b[i]*2+ch[i][j]-'0';
        sort(b+1,b+n+1);
        sort(a+1,a+n+1);
        for(i=1;i<=n;i++)
        {
            tmp=a[1]^b[i],cnt=0;
            for(j=1;j<=n;j++) c[j]=a[j]^tmp;
            while(tmp) tmp-=(tmp&(-tmp)),cnt++;
            sort(c+1,c+n+1);
            for(j=1;j<=n;j++) if(c[j]!=b[j]) break;
            if(j==n+1) ans=min(ans,cnt);
        }
        for(i=1;i<=n;i++) if(a[i]!=b[i]) break;
        if(i==n+1) ans=0;
        if(ans==l+1) printf("Impossible
");
        else printf("%lld
",ans);
    }
    return 0;
}

以上是关于2019.10.26 CSP%您赛第三场的主要内容,如果未能解决你的问题,请参考以下文章

2018年第四阶段组队训练赛第三场(BAPC2017 Preliminaries)

UPC2018组队训练赛第三场

问题 L: An Invisible Hand - (2018年第二阶段个人训练赛第三场)

Contest1593 - 2018-2019赛季多校联合新生训练赛第三场(部分题解)

2019.10.26 csp-s模拟测试88 反思总结

CSP核心代码片段记录