【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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章