DAY3 

钟皓曦来了!


 

网址压缩

【问题描述】

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

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

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

更具体来说,网址压缩的目标是希望将较长的网址变为更短的网址,例如我们可以将 https://www.baidu.com 压缩为 http://sb.cn/。为了更加形式化我们的问题,

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

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

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

3、相同的字符串压缩结果必须相同,不同的字符串压缩结果必须不同。

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

 【输入格式】

第一行一个整数????。

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

【输出格式】

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

【样例输入】

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。

 

题解

 

                                DAY 3模拟赛

 

 

首先,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 每轮倒茶都是同时开始同时结束的。

 

 

相关文章:

  • 2022-02-01
  • 2021-12-11
  • 2021-11-01
  • 2021-12-13
  • 2021-08-09
  • 2021-06-08
  • 2021-11-11
  • 2021-06-28
猜你喜欢
  • 2022-03-04
  • 2021-09-01
  • 2021-08-29
  • 2021-09-23
  • 2022-12-23
  • 2021-10-02
  • 2021-12-23
相关资源
相似解决方案