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 每輪倒茶都是同時開始同時結束的。

相關文章
相關標籤/搜索