【NOIP2017】SummerTraining0710

T1 並查集/二分圖染色
T2 DP+矩陣乘法優化
T3 數位DP
100+100+30=230 rank13
想出了T2矩陣乘法好開森,然並卵……
%sqj wsj zhz大佬AK怒噴水題
後附zhz大佬的蔑視

看題解或代碼的看目錄,有索引。

T1

問題 A: 七天使的通訊
時間限制: 2 Sec 內存限制: 256 MB
題目描述
n個天使排成一條直線,某些天使之間需要互相聯繫,他們之間的通訊可以通過黑白兩種通道中的一種;所有通道必須在直線同側(另一側是地面);爲了保證通訊效率,同種顏色的所有通道之間不能相交。請計算能否建立這種通訊方案。
輸入
第一行一個數T,表示接下來有T個詢問。
對於每個詢問:第一行兩個數n,m,分別表示有n個天使、需要建立通訊線路的天使有m對;接下來有m行,每行兩個數a、b,表示a、b兩個天使需要通訊。
輸出
對於每個詢問,輸出一行“sane”表示有可行方案、“non”表示無解

樣例輸入
1
7 5
1 3
2 7
3 4
7 4
6 5

樣例輸出
sane

提示

【樣例解釋】

樣例中共有一個詢問。

在(1,3)、(4,7)、(5,6)之間連黑色通道,在(2,7)、(3,4)之間連白色通道,每條通道都成功建立,且同種顏色的通道沒有相交,所以輸出sane。

【數據規模和約定】

對於 20%的數據,1<=n<=50,1<=m<=15

對於 50%的數據,1<=n<=1000,1<=m<=300

對於 100%的數據,1<=n<=5000,1<=m<=1000,1<=T<=10,1<=a<=n,1<=b<=n

數據保證每對(a,b)不重複,且a不等於b

【提示】

當兩條線路有一對相同的端點時,這兩條線路不相交。

也就是說,對於線路(a,b)和線路(c,d)(a

Solution

將每個通道設爲一個節點,先暴力判斷每兩條通道如果是同種顏色會不會相交,如果會相交就在這兩個節點之間連無向邊,說明它們不能爲同種顏色(必須在二分圖兩邊)。然後對組成的無向圖進行二分圖判定(DFS染色),如果染色成功說明該圖是一個二分圖,即有解,否則無解。
PS:爲什麼在我眼中顯然的並查集竟然是二分圖……

Code

並查集

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
int a[MAXN],b[MAXN];
int fa[MAXN],val[MAXN],rank[MAXN];
int n,m;

inline bool pd(int a,int b,int c,int d)
{
    return (a<c&&c<b&&b<d || c<a&&a<d&&d<b);
}
int getfa(int x)
{
    if (fa[x]==x) return fa[x];
    int y=fa[x];
    fa[x]=getfa(fa[x]);
    val[x]=(val[x]+val[y])&1;   
    return fa[x];
}
bool solve()
{
    for (int i=1;i<=m;i++) fa[i]=i,val[i]=0,rank[i]=0;
    for (int i=1;i<m;i++)
        for (int j=i+1;j<=m;j++)
            if (pd(a[i],b[i],a[j],b[j]))
            {
                int x=i,y=j;
                int fx=getfa(x),fy=getfa(y);
                if (fx!=fy)
                {
                    if (rank[fx]<rank[fy])
                    {
                        fa[fx]=fy;
                        val[fx]=(val[y]+1-val[x])&1;
                    }
                    else
                    {
                        fa[fy]=fx;
                        val[fy]=(val[x]+1-val[y])&1;
                        rank[fx]++;
                    }
                }
                else if (val[x]==val[y]) return false;
            }
    return true;
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            if (a[i]>b[i]) swap(a[i],b[i]);
        }
        if (solve()) printf("sane\n");
        else printf("non\n");
    }
    //system("pause");
    return 0;
}

二分圖染色

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
int a[MAXN],b[MAXN],color[MAXN];
int Head[MAXN],Next[MAXN*MAXN],To[MAXN*MAXN];
bool vis[MAXN];
int n,m,tot;

inline bool pd(int a,int b,int c,int d)
{
    return (a<c&&c<b&&b<d || c<a&&a<d&&d<b);
}
void add(int x,int y)
{
    tot++;
    Next[tot]=Head[x];
    Head[x]=tot;
    To[tot]=y;
}
bool dfs(int u)
{
    if (color[u]==-1) color[u]=1;
    vis[u]=true;
    for (int i=Head[u];i;i=Next[i])
    {
        int v=To[i];
        if (vis[v] && color[v]==color[u]) return false;
        if (!vis[v]) color[v]=1^color[u];
        if (!vis[v] && !dfs(v)) return false;
    }
    return true;
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        tot=0;
        memset(Head,0,sizeof(Head));
        scanf("%d%d",&n,&m);
       for (int i=1;i<=m;i++)
       {
            scanf("%d%d",&a[i],&b[i]);
            if (a[i]>b[i]) swap(a[i],b[i]);
        }
        for (int i=1;i<=m;i++)
            for (int j=i+1;j<=m;j++)
                if (pd(a[i],b[i],a[j],b[j])) add(i,j),add(j,i);
        bool flag=false;
        memset(color,-1,sizeof(color));
        memset(vis,false,sizeof(vis));
        for (int i=1;i<=m;i++)
            if (!vis[i] && !dfs(i)) {printf("non\n"); flag=true; break;}
        if (!flag) printf("sane\n");
    }
    return 0;
}

T2

問題 B: 都市環遊
時間限制: 1 Sec 內存限制: 512 MB
題目描述
因爲SJY乾的奇怪事情過多,SJY收到了休假的通知,於是他準備在都市間來回旅遊。SJY有一輛車子,一開始行駛性能爲0,每過1時間行駛性能就會提升1點。每個城市的道路都有性能要求。SJY一共有t時間休息,一開始他位於1號城市(保證1號城市道路要求爲0),他希望在n號城市結束旅程。每次穿過一條城市間的路會花費1時間,當然他也可以停留在一個城市不動而花費1時間。當且僅當車子的行駛性能大於等於一個城市,我們才能到達那裏。SJY希望知道,旅遊的方案模10086後的答案。(只要在某一時刻通過的道路存在一條不相同,就算不同的方案)
輸入
第一行三個數n,m,t,表示有n個城市m條道路t時間。
第二行n個數,hi表示第i個城市的道路性能要求。
第三到m+2行,每行兩個數u,v,表示城市u與城市v之間有一條單向道路連接(可能有重邊)。
輸出
包括一個數字,表示旅遊的方案模10086。
樣例輸入
5 17 7
0 2 4 5 3
1 2
2 1
1 3
3 1
1 4
4 1
4 5
5 4
5 3
4 1
2 1
5 3
2 1
2 1
1 2
2 1
1 3

樣例輸出
245

提示

【數據規模和約定】

對於20%的數據,n<=10,t<=80;


對於50%的數據,n<=30,t<=80;


對於100%的數據,n<=70,m<=1000,t<=100000000,hi<=70。

Solution

算法1:
暴力 ,搜索全部路徑,發現每一步的行動可以用一個轉移矩陣表示,每一步相當於乘上矩陣。

算法2:
由算法1得到,將每一步移動的轉移矩陣都求出來,然後進行矩陣乘法,時間複雜度 O(n^3*t)

算法 3:
在算法2的基礎上利用矩陣快速冪優化矩陣乘法,時間複雜度O(n^3*logt)

PS:爲什麼標解中每一步都是矩陣乘法啊,我覺得應該是DP,設f[i][j]表示到i城市j時間的方案數,在j>=t時f[i][j]+=f[k][j-1] 有一條k—>j的路徑,然後纔想到矩陣乘法優化的……莫非是我太菜了?

Code

#include<bits/stdc++.h> 
using namespace std; 
#define MOD 10086 
const int MAXN=110; 
int f[MAXN][MAXN],a[MAXN][MAXN],h[MAXN]; 
int n,m,T,max_h; 
struct matrix 
{ 
    int a[MAXN][MAXN]; 
    int row,col; 
    matrix(int n=0) //Identity matrix;
    {
        row=col=n;
        memset(a,0,sizeof(a));
        for (int i=1;i<=n;i++) a[i][i]=1;
    }
    friend  matrix operator * (const matrix &a,const matrix &b) 
    { 
        if (a.col!=b.row) {cout<<"error!";} 
        matrix c; 
        c.row=a.row; c.col=b.col; 
        for (int i=1;i<=a.row;i++) 
            for (int j=1;j<=b.col;j++) 
            { 
                c.a[i][j]=0; 
                for (int k=1;k<=a.col;k++) 
                { 
                    c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%MOD; 
                    //if (c.a[i][j]>=MOD) c.a[i][j]%=MOD; 
                } 
            } 
        return c;        
    } 
}A,F; 

void pre() 
{ 
    scanf("%d%d%d",&n,&m,&T); 
    for (int i=1;i<=n;i++) 
        scanf("%d",&h[i]);
    for (int i=1;i<=m;i++) 
    { 
        int x,y; 
        scanf("%d%d",&x,&y); 
        a[x][y]++; 
    } 
    max_h=-1; 
    for (int i=1;i<=n;i++) a[i][i]=1,max_h=max(max_h,h[i]); 
} 
matrix power(matrix a,int b) 
{ 
    matrix ans=matrix(n);
    while (b) 
    { 
        if (b&1) ans=ans*a; 
        a=a*a; 
        b>>=1;         
    } 
    return ans;  
} 
void solve() 
{ 
    f[1][0]=1; 
    for (int j=1;j<=min(T,max_h);j++) 
    { 
        for (int i=1;i<=n;i++) 
        { 
            f[i][j]=0; 
            if (j>=h[i]) 
                for (int k=1;k<=n;k++) 
                    f[i][j]=(f[i][j]+f[k][j-1]*a[k][i])%MOD; 
        }    
    } 
    if (T<=max_h) {printf("%d",f[n][T]); return;}
    // the rest use matrix mul;
    for (int i=1;i<=n;i++) 
        for (int j=1;j<=n;j++) 
            A.a[i][j]=a[i][j]; 
    A.row=A.col=n; 
    for (int i=1;i<=n;i++) 
        F.a[1][i]=f[i][max_h]; 
    F.row=1; F.col=n; 
    A=power(A,T-max_h); 
    F=F*A; 
    cout<<F.a[1][n]; 
} 
int main() 
{ 
    pre(); 
    solve(); 
    //system("pause");
    return 0; 
} 

T3

問題 C: 大水題
時間限制: 1 Sec 內存限制: 512 MB
題目描述
dzy 定義一個n^2 位的數的生成矩陣A 爲一個大小爲n*n 且Aij 爲這個數的第i*n+j-n位的矩陣。
現在dzy 有一個數n^2 位的數k,他想知道所有小於等於k 的數的n*n 生成矩陣有多少種。(如果不足n^2 位則補前綴零)
輸入
第一行一個數n,第二行一個n^2 位的數k
輸出
僅一行表示答案,答案可能很大,你只需輸出答案對10^9 + 7 取模後的結果。
樣例輸入
2
1000

樣例輸出
954

提示

【數據規模和約定】

對於30% 的數據n<=2

對於100% 的數據n <=1000,且n爲偶數

【提示】

如果兩個生成矩陣在其中一個旋轉180 度後可以重疊,則稱這兩個矩陣是相同的。

Solution

這裏寫圖片描述
這裏寫圖片描述

這題其實我也還沒動,先上標稱吧,
PS:zhz大佬的蔑視
這裏寫圖片描述

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int x[1000010],mo=1000000007,n,f[1000010][2][2];
char ch[1000011];
int solve(){
    memset(f,0x00,sizeof f); f[0][1][0]=1;
    for (int i=1;i<=n*n;i++){
        for (int k=0;k<=9;k++)
            if (k<x[n*n-i+1])
                f[i][0][0]=(f[i][0][0]+f[i-1][0][0]+f[i-1][0][1])%mo;
            else if (k>x[n*n-i+1])
                f[i][0][1]=(f[i][0][1]+f[i-1][0][0]+f[i-1][0][1])%mo;
            else {
                f[i][0][0]=(f[i][0][0]+f[i-1][0][0])%mo;
                f[i][0][1]=(f[i][0][1]+f[i-1][0][1])%mo;
            }
        for (int k=0;k<x[i];k++)
            if (k<x[n*n-i+1])
                f[i][0][0]=(f[i][0][0]+f[i-1][1][0]+f[i-1][1][1])%mo;
            else if (k>x[n*n-i+1])
                f[i][0][1]=(f[i][0][1]+f[i-1][1][0]+f[i-1][1][1])%mo;
            else {
                f[i][0][0]=(f[i][0][0]+f[i-1][1][0])%mo;
                f[i][0][1]=(f[i][0][1]+f[i-1][1][1])%mo;
            }
        if (x[i]<x[n*n-i+1]) f[i][1][0]=(f[i-1][1][0]+f[i-1][1][1])%mo;
        else if (x[i]>x[n*n-i+1]) f[i][1][1]=(f[i-1][1][0]+f[i-1][1][1])%mo;
        else{
            f[i][1][0]=f[i-1][1][0]; f[i][1][1]=f[i-1][1][1];
        } 
    }
    f[n*n][0][0]--;
    return (f[n*n][0][0]+f[n*n][1][0])%mo;
}
int solve2(){
    int ans=0;
    for (int i=1;i<=n*n/2;i++) ans=(ans*10+x[i])%mo;
    for (int i=n*n/2+1;i<=n*n;i++) if (x[n*n-i+1]<x[i]) return ans; else if (x[n*n-i+1]>x[i]) return (ans-1+mo)%mo; 
    return ans;
}
int main(){
    freopen("water.in","r",stdin);
    freopen("water.out","w",stdout);
    scanf("%d",&n); scanf("%s",ch+1);
    for (int i=1;i<=n*n;i++) x[i]=ch[i]-'0';
    int ans=0;
    for (int i=1;i<=n*n;i++) ans=(ans*10+ch[i]-'0')%mo; 
    int k1=(1ll*(solve()-solve2()+mo)*500000004%mo+mo)%mo; 
    ans=((ans-k1)%mo+mo)%mo; 
    cout<<ans<<endl; return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章