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

 

 

相关文章: