11.9模拟赛

Problem 1 Graph (graph.cpp/c/pas)

 

【题目描述】

给出 N 个点,M 条边的有向图,对于每个点 v,求 A(v) 表示从点 v 出发,能到达的编号最大的点。

 

【输入格式】

第 1 行,2 个整数 N,M。 接下来 M 行,每行 2 个整数 Ui,Vi,表示边 ⟨Ui,Vi⟩。点用 1,2,...,N 编号。

 

【输出格式】

N 个整数 A(1),A(2),...,A(N)。

 

【样例输入】

4 3

1 2

2 4

4 3

【样例输出】

4 4 3 4

【数据范围】

对于 60% 的数据,1 ≤ N,K ≤ 10^3

对于 100% 的数据,1 ≤ N,M ≤ 10^5。

 

题解:

暴力+错误tarjan缩点

先写的缩点,对拍半天,补了n个漏洞,跑大数据平均10个WA3个

两个程序就一起交了。略丑。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define N 100008
using namespace std;
int n,m,sumedge;
queue<int>q;
int head[N];

int ans[N],vis[N];

int top,tim,sumcol;

int low[N],dfn[N],Stack[N],instack[N],bel[N],mx[N];

int cd[N],rd[N];

inline int read(){
    char ch=getchar();int x=0,f=1;
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[N];

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

void bfs(int x){
    memset(vis,0,sizeof(vis));
    vis[x]=true;
    while(!q.empty())q.pop();
    q.push(x);
    while(!q.empty()){
        int now=q.front();q.pop();
        ans[x]=max(ans[x],now);
        for(int i=head[now];i;i=edge[i].nxt){
            int v=edge[i].y;
            if(!vis[v]){
                q.push(v);vis[v]=1; 
            }
        }
    }
}

void tarjian(int x){
    low[x]=dfn[x]=++tim;
    Stack[++top]=x;instack[x]=true;
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(instack[v])low[x]=min(low[x],dfn[v]);
        else if(!dfn[v]){
            tarjian(v);
            low[x]=min(low[x],low[v]);
        }
    }
    if(low[x]==dfn[x]){
        sumcol++;
        while(Stack[top+1]!=x){
            bel[Stack[top]]=sumcol;
            instack[Stack[top]]=false;
            mx[sumcol]=max(mx[sumcol],Stack[top]);
            top--;
        }
    }
}

void dfs(int x){
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(bel[x]==bel[v])continue;
        dfs(v);
        ans[bel[x]]=max(ans[bel[x]],ans[bel[v]]);
    }
    return;
}

int main(){
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=m;i++){
        int x,y;
        x=read();y=read();
        add(x,y);
    }
    if(n<=3000){
        for(int i=1;i<=n;i++)bfs(i);
        for(int i=1;i<=n;i++)printf("%d ",ans[i]);
        return 0;
        fclose(stdin);fclose(stdout);
    }
    for(int i=1;i<=n;i++)if(!dfn[i])tarjian(i);
    for(int x=1;x<=n;x++){
        for(int i=head[x];i;i=edge[i].nxt){
            int v=edge[i].y;
            if(bel[v]!=bel[x]) cd[bel[x]]++,rd[bel[v]]++;
        }
    }
      for(int i=1;i<=n;i++)ans[bel[i]]=mx[bel[i]];
    for(int i=1;i<=n;i++)if(!rd[bel[i]])dfs(i);
    for(int i=1;i<=n;i++)printf("%d ",max(ans[bel[i]],mx[bel[i]]));
    fclose(stdin);fclose(stdout);
    return 0;
}
80

 缩点后,记录每个点的最大点权,那么某个点的能到达的最大点权就是

所在环的最大点权和能到达的最大点权的Max。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define N 100008
using namespace std;

int n,m,sumedge,sume;
queue<int>q;
int head[N],hed[N];

int ans[N],vis[N];

int top,tim,sumcol;

int low[N],dfn[N],Stack[N],instack[N],bel[N],mx[N];

int cd[N],rd[N];

inline int read(){
    char ch=getchar();int x=0,f=1;
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[N];

struct E{
    int x,y,nxt;
    E(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}e[N];

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

void add_(int x,int y){
    e[++sume]=E(x,y,hed[x]);
    hed[x]=sume;
}

void tarjian(int x){
    low[x]=dfn[x]=++tim;
    Stack[++top]=x;instack[x]=true;
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(instack[v])low[x]=min(low[x],dfn[v]);
        else if(!dfn[v]){
            tarjian(v);
            low[x]=min(low[x],low[v]);
        }
    }
    if(low[x]==dfn[x]){
        sumcol++;
        while(Stack[top+1]!=x){
            bel[Stack[top]]=sumcol;
            instack[Stack[top]]=false;
            mx[sumcol]=max(mx[sumcol],Stack[top]);
            top--;
        }
    }
}

void dfs(int x){
    if(vis[x])return ;
    vis[x]=true;ans[x]=mx[x];
    for(int i=hed[x];i;i=e[i].nxt){
        int v=e[i].y;
        dfs(v);
        ans[x]=max(ans[x],ans[v]);
    }
}

int main(){
//    freopen("graph.in","r",stdin);
//    freopen("graph.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=m;i++){
        int x,y;
        x=read();y=read();
        add(x,y);
    }
    for(int i=1;i<=n;i++)if(!dfn[i])tarjian(i);
    for(int x=1;x<=n;x++){
        for(int i=head[x];i;i=edge[i].nxt){
            int v=edge[i].y;
            if(bel[v]!=bel[x]) {
                cd[bel[x]]++,rd[bel[v]]++;
                add_(bel[x],bel[v]);
            }
        }
    }
      for(int i=1;i<=n;i++)ans[bel[i]]=mx[bel[i]];
    for(int i=1;i<=sumcol;i++)if(!vis[i])dfs(i);
    for(int i=1;i<=n;i++)printf("%d ",ans[bel[i]]);
    return 0;
}
AC缩点+dp

 

Problem 2 Incr(incr.cpp/c/pas)

 

【题目描述】

数列 A1,A2,...,AN,修改最少的数字,使得数列严格单调递增。

 

【输入格式】

第 1 行,1 个整数 N

第 2 行,N 个整数 A1,A2,...,AN

【输出格式】

1 个整数,表示最少修改的数字

 

【样例输入】

3

1 3 2

【样例输出】

1

【数据范围】

对于 50% 的数据,N ≤ 10^3

对于 100% 的数据,1 ≤ N ≤ 10^5,1 ≤ Ai ≤ 10^9

 

题解:

 n-最长上升子序列长度

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100008
using namespace std;

int n,ans;

int a[N],dp[N];

int main(){
    freopen("incr.in","r",stdin);
    freopen("incr.out","w",stdout);
    scanf("%d",&ans);
    for(int i=1;i<=ans;i++)scanf("%d",&a[i]);
    for(int i=1;i<=ans;i++)dp[i]=1;
    for(int i=1;i<=ans;i++)
     for(int j=1;j<i;j++)
       if(a[j]<a[i])dp[i]=max(dp[i],dp[j]+1);
    for(int i=1;i<=ans;i++)n=max(n,dp[i]);
    ans-=n;
    cout<<ans<<endl;
    fclose(stdin);fclose(stdout);
    return 0;
} 
50
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=0x3f3f3f3f;
int n,a[1000009],dp[1000009];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    memset(dp,0x3f,sizeof(dp));
    for(int i=1;i<=n;i++)
    {
        int p=upper_bound(dp+1,dp+n+1,a[i])-dp;
        if(a[i]!=dp[p-1])//严格上升序列 
        dp[p]=a[i];
    }
    for(int i=1;i<=n+1;i++)
    {
        if(dp[i]==maxn)
        {
            printf("%d\n",n-(i-1));
            return 0;
        }
    }
    return 0;
}
AC

 

Problem 3 Permutation (permutation.cpp/c/pas)

 

【题目描述】

将 1 到 N 任意排列,然后在排列的每两个数之间根据他们的大小关系插入“>”和“<”。

问在所有排列中,有多少个排列恰好有K“<”。 

例如排列(3, 4, 1, 5, 2)

3 < 4 > 1 < 5 > 2

共有2“<”

 

【输入格式】

N,K

 

【输出格式】

答案

 

【样例输入】

5 2

【样例输出】

66

【数据范围】

20%:N <= 10

50%:答案在0..2^63-1内 

100%:K < N <= 100

 

题解:

20暴力

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;

int n,k;

int a[100];

LL ans;

int main(){
    freopen("permutation.in","r",stdin);
    freopen("permutation.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)a[i]=i;
    do{
        int js=0;
        for(int i=2;i<=n;i++) if(a[i]>a[i-1])js++;
        if(js==k)ans++;
    }while(next_permutation(a+1,a+n+1));
    cout<<ans<<endl;
    fclose(stdin);fclose(stdout);
    return 0;
}
20

 f[i][j]表示前i个位置放j个小于号。

i个数一共有i+1个位置,那么插入当前第i个数

可能会产生小于号,也可能不会产生小于号

不会产生小于号的情况是插在小于号上,一共有j+1个位置,

会产生的有i-j个位置。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int n,k;

int f[1020][1020];

int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)f[i][0]=1;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=k;j++)
      f[i][j]+=(f[i-1][j]*(j+1)+f[i-1][j-1]*(i-j))%2015;
    cout<<f[n][k];
    return 0;
}
AC

 

posted @ 2017-11-09 15:23  ANhour  阅读(356)  评论(0编辑  收藏  举报