DAY 3模拟赛

DAY3 

钟皓曦来了!


 

T1

网址压缩

【问题描述】

你是能看到第一题的 friends 呢。           ——hjaios

众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。算法

网址压缩是现实世界的一个重要问题,随着网址数量的日益增加,长网址的存储已经给现现在的网络带来了巨大的压力。为了解决这个问题,网址压缩已经成为了一件迫在眉睫的事情(以上都是我瞎编的)。网络

更具体来讲,网址压缩的目标是但愿将较长的网址变为更短的网址,例如咱们能够将 https://www.baidu.com 压缩为 http://sb.cn/。为了更加形式化咱们的问题,测试

咱们如今的任务是给定N个只包含小写字母的字符串,你须要输出对这N个字符串进行压缩的结果。你可使用任意的压缩算法,但你须要保证知足以下的性质:spa

一、压缩的结果字符串仍然只有小写字母。code

二、压缩的结果不能是空串,且长度必需要比原来的字符串短。orm

三、相同的字符串压缩结果必须相同,不一样的字符串压缩结果必须不一样。blog

任意知足上述条件的压缩方法都是正确的,因此你的目标就是对给定的N个字符串进行压缩。数据保证有解排序

 【输入格式】

第一行一个整数𝑁ip

接下来𝑁行每行一个字符串。

【输出格式】

输出𝑁行每行一个字符串表明压缩的结果。

【样例输入】

4

jzm

bilibili

hhhhh

jzm

【样例输出】

ha

bilibil

hhhh

ha

【数据规模与约定】

对于100%的数据,𝑁 ≤ 1000,字符串长度≤ 50。大部分测试点直接随机造

数据,小部分测试点为构造数据。

 

题解

全损压缩

先把全部的字符串读入,而后按照长度从小到大排序,而后一个一个压缩(a,b,c,...,z,aa,ab...这样子)

好像还有一个奇技淫巧,只须要排序以后把第一个字符删掉就能够过这道题

然鹅并无卡掉这种作法

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<map>
#include<string>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxn=1010;

map<string,string> ma;

string z[maxn];

int n,l=1,res[233],y[maxn];

bool cmp(int a,int b)
{
    return z[a]<z[b];
}

int main()
{
    cin >> n;
    for (int a=1;a<=n;a++)
    {
        cin>>z[a];
        y[a]=a;
    }
    res[1]=1;
    sort(y+1,y+n+1,cmp);
    for (int a=1;a<=n;a++)
    {
        string now = z[y[a]];
        if (ma.count(now)!=0) ;
        else
        {
            string cur = "";
            for (int a=l;a>=1;a--)
                cur = cur + (char)(res[a]+'a'-1);
            ma[now] = cur;
            res[1]++;
            for (int a=1;a<=l;a++)
                if (res[a]>26)
                {
                    res[a]=1;
                    res[a+1]++;
                }
            if (res[l+1]) l++;
        }
    }
    for (int a=1;a<=n;a++)
        cout << ma[z[a]] << endl;

    return 0;
}

 

 

 

T2

 

异构体

 

【问题描述】

 

你是能看到第二题的 friends 呢。

 

——aoao

 

众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。

 

Paradeus 是一个新兴的宗教组织,该组织包含了𝑁 − 1个 Nyto,以及一个Mercurows 总共𝑁我的组成。每一个 Nyto 都是被其余某我的传教而进入的 Paradeus,而 Mercurows 是宗教的创立者,也就是说 Mercurows 并无被任何人拉进组织。这张记录了每一个人是由谁拉进传销组织的记录被视为 Paradeus 的教义,一直被广为传颂。

 

然而,随着岁月的流逝,有不法分子开始对 Paradeus 的教义发动了攻击。不法分子在 Paradeus 的教义上添加了一条记录(𝑎, 𝑏),表明𝑏是由𝑎介绍入教的。这条记录的加入致使 Nyto 们发现教义已经不合法了。为了复兴教义,教徒们决定找到这条被不法分子加入的记录,并将其删除以恢复教义的荣光。更具体的说,如今给定𝑁对记录(𝑎𝑖, 𝑏𝑖)表明是𝑎𝑖将𝑏𝑖拉入教的。注意这𝑁条记录包含了被不法分子添加的那一条。如今咱们但愿你找到某一条记录,使得删掉这条记录以后剩下的𝑁 − 1条记录可以造成合法的教义。要注意的是,教义并没有标注谁是 Mercurows,因此任何人都有多是 Mercurows。

 

一棵外向树(全部边从根向外指),加一条边,找出这条边让他编号尽量大

【输入格式】

第一行一个数𝑁表明人数。接下来𝑁行每行两个数𝑎𝑖, 𝑏𝑖表明一条记录。

【输出格式】

一行一个数表明删掉第几条记录可以使得教义合法。若是有多种方案,输出编号最大的方案。数据保证有解。

【样例输入】

3

1 2

1 3

2 3

【样例输出】

3

【数据规模与约定】

对于40%的数据,𝑁 ≤ 1000

对于另外20%的数据,可能成为 Mercurows 的人必定只有一个。

 

对于100%的数据,1 ≤ 𝑁 ≤ 10^5

 

题解:

讨论

1.知道根节点是谁  看看有没有入度为0的点

 横叉边和返祖边

 其实是一种状况,就是有一个点入度为2

 暴力删边两次看看连不连通(然而我直接贪心取了最大的,而后100-->80)

2.不能找到一个入度为0的点 -->必定有环

 在这个环上删除任何一个边均可以

    怎么找环?BFS,并查集,tarjan

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=100010;

int n,en,head,tail,in[maxn],ex[maxn][2],q[maxn],f[maxn],pre[maxn][2];

bool use[maxn];

struct edge
{
    int s,e,p;
    edge *next;
}*v[maxn],ed[maxn];

void add_edge(int s,int e,int p)
{
    en++;
    ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->p=p;
    in[e]++;
    ex[en][0]=s;ex[en][1]=e;
}

int get(int p)
{
    if (f[p]==p) return p;
    else return get(f[p]);
}

void init()
{
    en=0;
    memset(v,0,sizeof(v));
    memset(in,0,sizeof(in));
}

void read()
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
    {
        int s,e;
        scanf("%d%d",&s,&e);
        add_edge(s,e,a);
    }
}

bool work1(int p,int d)
{
    memset(use,false,sizeof(use));
    use[p]=true;
    head=tail=1;
    q[1]=p;
    for (;head<=tail;)
    {
        int p=q[head++];
        for (edge *e=v[p];e;e=e->next)
            if (e->p!=d)
            {
                if (use[e->e]) return false;
                use[e->e]=true;
                pre[e->e][0]=p;
                pre[e->e][1]=e->p;
                q[++tail]=e->e;
            }
    }
    for (int a=1;a<=n;a++)
        if (!use[a]) return false;
    return true;
}

int work2(int p)
{
    memset(use,false,sizeof(use));
    use[p]=true;
    head=tail=1;
    q[1]=p;
    for (;head<=tail;)
    {
        int p=q[head++];
        for (edge *e=v[p];e;e=e->next)
        {
            if (use[e->e]) return e->p;
            use[e->e]=true;
            q[++tail]=e->e;
        }
    }
    return -1;
}

int work()
{
    for (int a=1;a<=n;a++)
        if (in[a]==2)
        {
            int p1=-1,p2=-1;
            for (int b=1;b<=n;b++)
                if (ex[b][1]==a)
                {
                    if (p1==-1) p1=b;
                    else p2=b;
                }
            if (p1>p2) swap(p1,p2);
            for (int b=1;b<=n;b++)
                if (in[b]==0)
                {
                    if (work1(b,p2)) return p2;
                    else return p1;
                }
        }
    for (int a=1;a<=n;a++)
        if (in[a]==0) return work2(a);
    for (int a=1;a<=n;a++)
        f[a]=a;
    for (int a=1;a<=n;a++)
    {
        int f1=get(ex[a][0]);
        int f2=get(ex[a][1]);
        if (f1==f2)
        {
            work1(ex[a][1],a);
            int p=ex[a][1],ans=a;
            while (p!=ex[a][1])
            {
                ans=max(ans,pre[p][1]);
                p=pre[p][0];
            }
            return ans;
        }
        f[f1]=f2;
    }
    return -1;
}

int main()
{
    init();
    read();
    int p=work();
    printf("%d\n",p);

    return 0;
}

 

 

个人菜鸡80分代码(个人找环是在开始建了双向边,而后直接dfs,可是在上面的第一种状况直接取了max致使80pts)

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
const int N=100005;

int n;
int head[N],ecnt,ver[N];
struct edge
{
    int to,nxt;
}edg[N<<1];
inline void add(int u,int v)
{

    edg[++ecnt].to=v;
    edg[ecnt].nxt=head[u];
    head[u]=ecnt;
    edg[ecnt+n].to=u;
    edg[ecnt+n].nxt=head[v];
    head[v]=ecnt+n;
}

int in[N],out[N];
bool flag=0;

void checkout()
{
    for(int i=1;i<=n;i++)
    {
        if(out[i]>1)
        {
            int maxn=-1;
            for(int j=head[i];j;j=edg[j].nxt)
            {
                if(j<=n)
                maxn=max(maxn,j);//这里不对,应该删掉每一条边以后判断连通性 
            }
            printf("%d\n",maxn);
            flag=1;
        }
    }
}

bool huan[N];
bool vis[N];
int dfn[N];
int fa[N];
int cnt,sum;
int ans[N];

void dfs(int now)
{
    dfn[now]=++cnt;
    for(int i=head[now],v;i;i=edg[i].nxt)
    {
        v=edg[i].to;
        if(v==fa[now]) continue;
        if(dfn[v]) 
        {
            //if(dfn[v]<dfn[now]) continue;
            ans[++sum]=v,huan[v]=1;
            for(;v!=now;v=fa[v]) ans[++sum]=fa[v],huan[fa[v]]=1;
        }
        else fa[v]=now,dfs(v);
    }
}

void checkhuan()
{
    dfs(1);
}

int zhx=-1;

void getans(int x)
{
    for(int i=head[x];i;i=edg[i].nxt)
    {
        if(i<=n)
        {
            int v=edg[i].to;
            if(huan[v]==1&&vis[v]==0)
            {
                vis[v]=1;
                zhx=max(zhx,i);
                getans(v);
            }
        }
    }
}

int main()
{
//    freopen("remove.in","r",stdin);
//    freopen("remove.out","w",stdout);
    scanf("%d",&n);
    for(int i=1,u,v;i<=n;i++)
    {
        scanf("%d%d",&u,&v);
        add(v,u);
        in[u]++;
        out[v]++;
    }    
    checkout();
    if(flag==1) return 0;
    checkhuan();

    getans(ans[1]);
    for(int i=1;i<=sum;i++) printf("%d ",ans[i]);
}



/*
5
5 2
1 2
2 3
3 1
2 4
*/

 

 

T3

给大佬递茶

【问题描述】

你是能看到第三题的 friends 呢。

——laekov

众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。

 

自古以来,递茶就是一种传统美德,而给大佬递茶更是普遍活跃于表情包的存在。如今为了模拟给大佬递茶的工做,Alice Bob 开始了递茶操做。一开始Alice Bob 都有一个杯子里面装了𝑁吨的茶。如今每次 Alice 会等几率地随机向垃圾桶里面倒入4𝐾, 3𝐾, 2𝐾或者𝐾吨的茶,而且若是 Alice 倒了𝑥吨的茶,Bob 就会向垃圾桶里面导入4𝐾𝑥吨的茶。注意每次操做的时候 Alice 或者 Bob 的茶有可能不够多,这个时候就能倒多少到多少。如今问 Alice 在四种操做彻底等几率的状况下,Alice 先把本身的茶倒光的几率加上 Alice Bob 同时把茶倒光的几率的一半是多少。注意,Alice Bob 每轮倒茶都是同时开始同时结束的。

【输入格式】

第一行两个整数𝑁,𝐾

【输出格式】

输出一行一个六位实数表明答案。

【样例输入】

2 1

【样例输出】

0.625000

【数据规模与约定】

对于30%的数据,1 ≤ 𝑁,𝐾 ≤ 100

对于60%的数据,1 ≤ 𝑁,𝐾 ≤ 1000

对于另外20%的数据,𝐾 = 1

 

对于100%的数据,1 ≤ 𝑁,𝐾 ≤ 10^9

 

题解

 

                                

 

 

首先,k其实没有区别

N=10,k=5和n=2,k=1是同样的

把读进来的n变成n/k(上取整),k变成1

给你一个数,问另一个数(要注意这种问题能够经过某种玄学的方式找规律)

打表???

1. 爆搜

能够加上记忆化 f[i][j]表示a里面有i,b里面有j的几率是多少

最后求f[n][n]

F[0][j]=1,f[i][0]=0,f[0][0]=0.5

转移  f[i][j]=0.25*(f[i-4][j]+f[i-3][j-1]+f[i-2][j-2]+f[i-1][j-3])  70pts

复杂度n^2

打表:随着n的增加,几率在增加

还有:答案只要求保留六位小数

因此,当n>?时  只须要输出1.000000??????

什么玄学东西????

 

#include<cstdio>
#include<cstdlib>
#include<cstring>

using namespace std;

const int maxn=201;

int n,k;

double f[maxn][maxn];

bool g[maxn][maxn];

double dfs(int n,int m)
{
    if (n<=0 && m<=0) return 0.5;
    if (n<=0) return 1.0;
    if (m<=0) return 0.0;
    if (g[n][m]) return f[n][m];
    g[n][m]=true;
    for (int a=1;a<=4;a++)
        f[n][m]+=0.25*dfs(n-a,m-4+a);
    return f[n][m];
}

double work(int n,int k)
{
    if (n/k>=maxn) return 1.0;
    return dfs((n/k)+(n%k?1:0),(n/k)+(n%k?1:0));
}

int main()
{
    scanf("%d%d",&n,&k);
    printf("%.6lf\n",work(n,k));

    return 0;
}

 

 

 

给大佬递茶

【问题描述】

你是能看到第三题的 friends 呢。

——laekov

众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没

什么关系。

自古以来,递茶就是一种传统美德,而给大佬递茶更是普遍活跃于表情包的

存在。如今为了模拟给大佬递茶的工做,Alice Bob 开始了递茶操做。一开始

Alice Bob 都有一个杯子里面装了𝑁吨的茶。如今每次 Alice 会等几率地随机向

垃圾桶里面倒入4𝐾, 3𝐾, 2𝐾或者𝐾吨的茶,而且若是 Alice 倒了𝑥吨的茶,Bob

会向垃圾桶里面导入4𝐾 𝑥吨的茶。注意每次操做的时候 Alice 或者 Bob 的茶有

可能不够多,这个时候就能倒多少到多少。如今问 Alice 在四种操做彻底等几率

的状况下,Alice 先把本身的茶倒光的几率加上 Alice Bob 同时把茶倒光的概

率的一半是多少。注意,Alice Bob 每轮倒茶都是同时开始同时结束的。

相关文章
相关标签/搜索