1. 程式人生 > 實用技巧 >2020.11.17&18訓練記錄

2020.11.17&18訓練記錄

2020.11.17&18訓練記錄

2020.11.17

CSP考完之後直接AFO了((

yls表示要加強訓練了。。。。

轉回正題:

2020.11.17第一題:BZOJ1123——BLO-Blockade

讀題之後可以很快的分析出題目做法:

1.如果當前被考慮的點i不是整張圖的割點:

那麼i被刪除掉之後其他點心中毫無波瀾,因為跟他們沒有半毛錢關係,而無法互相到達的點對(注意是有序的)就只有其他點和這個犧牲的點(大霧

所以在這種情況下答案就是2*(n-1)

2.如果當前被考慮的點i是整張圖的割點:

那麼這個點的犧牲會導致整個帝國的權利動盪

整張圖會被分成若干個連通塊。我們不妨設第一個連通塊S1的大小是siz1,第二個連通塊S2的大小siz2,以此類推

那麼連通塊與連通塊之間是都不能到達的,所以此時我們的公式為:

siz1(n-siz1)+siz2(n-siz2)+……+sizk*(n-sizk)

注意最好還可能剩下一小部分的點所以也要加上,否則可能會WA

貼程式碼:

//Tarjan  割點 
//BLO-Blockade
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int AIRBUS_A330_941NX=100010;
const int AIRBUS_A330_841NX=500010;
int dfn[AIRBUS_A330_941NX],low[AIRBUS_A330_941NX],iscut[AIRBUS_A330_941NX];
int tot,ver[AIRBUS_A330_841NX<<1],head[AIRBUS_A330_841NX],nxt[AIRBUS_A330_841NX<<1];
int n,m;
int siz[AIRBUS_A330_941NX],ans[AIRBUS_A330_941NX];
int num;
int read()
{
    int x=0,f=1;
    char c=getchar();
    while (c<'0'||c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while (c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
void addedge(int x,int y)
{
    tot++;
    ver[tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void Tarjan(int p)
{
    num++;
    dfn[p]=low[p]=num;
    // iscut[p]=false;
    siz[p]=1;
    int flag=0,sum=0;
    for(int i=head[p];i;i=nxt[i])
    {
        int q=ver[i];
        if(!dfn[q])
        {
            Tarjan(q);
            siz[p]+=siz[q];
            low[p]=min(low[p],low[q]);
            if(low[q]>=dfn[p])
            {
                flag++;
                ans[p]+=siz[q]*(n-siz[q]);
                sum+=siz[q];
                if(flag>1 || p!=1) iscut[p]=true; 
            }
        }
        else low[p]=min(low[p],dfn[q]);
    }
    if(iscut[p]) ans[p]+=(sum+1)*(n-sum-1)+n-1;
    else ans[p]=2*n-2;
}
signed main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        addedge(x,y);
        addedge(y,x);
    }
    Tarjan(1);
    //for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
    for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
    return 0;
}

2020.11.17第二題:BZOJ2657——旅遊

讀題之後可以很快的分析出題目做法:

因為是三角形,所以這些三角形之間肯定有一些公共邊。

用這些公共邊來重新建圖,此時的樹的直徑即為所求的結果。

2020.11.18第一題:BZOJ1143——祭祀river

讀題之後可以很快的分析出題目做法:

本題要求的答案的定義其實與二分圖最大獨立集非常像,而題目中又有可能將圖分成2個點集,所以我們嘗試使用二分圖來做。

因為這些點不一定聯通所以我們要先Floyd傳遞閉包一下,之後再使用Hungary演算法來計算二分圖最大匹配數,由此計算出二分圖最大獨立集也就是此題的答案。

貼程式碼:

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
const int M=1010;
int match[N],tot;
bool use[N];
int n,m,ans;
int g[N][N];
int read()
{
    int x=0,f=1;
    char c=getchar();
    while (c<'0'||c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while (c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
bool DFS(int x)
{
    for(int i=1;i<=n;i++)
    {
        if(!use[i] && g[x][i])
        {
            use[i]=true;
            if(!match[i] || DFS(match[i]))
            {
                match[i]=x;
                return true;
            }
        }
    }
    return false;
}
void hungary()
{
    tot=0;
    for(int i=1;i<=n;i++)
    {
        memset(use,0,sizeof(use));
        ans-=DFS(i);
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        g[x][y]=1;
    }
    for(int i=1;i<=n;i++) g[i][i]=1;
    for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) g[i][j]|=g[i][k]&g[k][j];
    for(int i=1;i<=n;i++) g[i][i]=0;
    ans=n;
    hungary();
    printf("%d\n",ans);
    return 0;
}