20190717

  今天,我做出了模版题3道,练习题1道。

T1割点:

题目背景

割点

题目描述

给出一个nn个点,mm条边的无向图,求图的割点。

输入输出格式

输入格式:
 

第一行输入n,mn,m

下面mm行每行输入x,yx,y表示xxyy有一条边

输出格式:
 

第一行输出割点个数

第二行按照节点编号从小到大输出节点,用空格隔开

输入输出样例

输入样例#1: 复制

6 7

1 2

1 3

1 4

2 5

3 5

4 5

5 6

输出样例#1: 复制

1

5

说明

对于全部数据,n \le 20000n≤20000m \le 100000m≤100000

点的编号均大于00小于等于nn

tarjan图不一定联通。

#include<bits/stdc++.h>

using namespace std;

struct edge{

         int u,v;

}e[2000005];

bool v[200005],pd,zz[200005];

int n,m,f[200005],ne[2000005],dfn[200005],low[200005],tree[200005],now,front[200005],tot;

inline int read(){

         int k=0,f=1;

         char c=getchar();

         for(;!isdigit(c);c=getchar())

           if(c=='-')

             f=-1;

         for(;isdigit(c);c=getchar())

           k=k*10+c-'0';

         return k*f;

}

inline void dfs(int x){

         low[x]=dfn[x]=++now;

         v[x]=true;

         for(int i=f[x];i;i=ne[i])

           if(!v[e[i].v])

             tree[e[i].v]=x,dfs(e[i].v);

         return ;

}

inline void bfs(int x){

         v[x]=true;

         for(int i=f[x];i;i=ne[i]){

                   int y=e[i].v;

                   if(tree[y]==x||tree[x]!=y){

                            if(!v[y])

                              bfs(y);

                   }

                   if(tree[y]==x)

                     low[x]=min(low[x],low[y]);

                   else if(tree[x]!=y)

                            low[x]=min(low[x],dfn[y]);

         }

         return;

}

int main(){

         n=read();

         m=read();

         for(int i=1;i<=2*m;i+=2){

                   tree[i]=i,tree[i+1]=i+1;

                   e[i+1].v=e[i].u=read(),e[i+1].u=e[i].v=read();

                   ne[i]=f[e[i].u],f[e[i].u]=i;

                   ne[i+1]=f[e[i+1].u],f[e[i+1].u]=i+1;

         }

         for(int i=1;i<=n;i++){

                   if(!v[i])

                     dfs(i),front[++tot]=i,zz[i]=true;

         }

         memset(v,false,sizeof(v));

         for(int i=1;i<=tot;i++)

           bfs(front[i]);

         for(int k=1;k<=tot;k++)

                   for(int i=f[front[k]];i;i=ne[i]){

                            int y=e[i].v;

                            if(dfn[front[k]]<=low[y]){

                                     if(pd){

                                               cout<<front[k]<<endl;

                                               break;

                                     }

                                     else

                                       pd=true;

                            }

                   }

         for(int i=1;i<=n;i++){

                   if(zz[i])

                     continue;

                   for(int j=f[i];j;j=ne[j]){

                            int y=e[j].v;

                            if(low[y]>=dfn[i]){

                                     cout<<i<<endl;

                                     break;

                            }

                   }

         }

         return 0;

}

/*

9 11

6 9

8 9

6 8

6 7

1 6

1 5

2 5

4 5

3 4

2 3

1 2

*/

T2负环:

题目描述

暴力枚举/SPFA/Bellman-ford/奇怪的贪心/超神搜索

寻找一个从顶点1所能到达的负环,负环定义为:一个边权之和为负的环。

输入输出格式

输入格式:
 

第一行一个正整数T表示数据组数,对于每组数据:

第一行两个正整数N M,表示图有N个顶点,M条边

接下来M行,每行三个整数a b w,表示a->b有一条权值为w的边(若w<0则为单向,否则双向)

输出格式:
 

共T行。对于每组数据,存在负环则输出一行"YE5"(不含引号),否则输出一行"N0"(不含引号)。

输入输出样例

输入样例#1: 复制

2

3 4

1 2 2

1 3 4

2 3 1

3 1 -3

3 3

1 2 3

2 3 4

3 1 -8

输出样例#1: 复制

N0

YE5

说明

n\leq 2000n≤2000m\leq 3000m≤3000-10000\leq w\leq 10000−10000≤w≤10000T\leq 10T≤10建议复制输出格式中的字符串。 本题数据感谢@negiizhao的精心构造,请不要使用玄学算法 本题数据有更新

// luogu-judger-enable-o2

#include<bits/stdc++.h>

#define IL inline

#define RI register int

#define N 100086

#define clear(a) memset(a,0,sizeof a)

#define rk for(RI i=1;i<=n;i++)

using namespace std;

IL void read(int &x)

{

    int f=1;x=0;char s=getchar();

    while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}

    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}

    x*=f;

}

int n,m,T;

struct code{int u,v,w;}edge[N];

bool vis[N];

int head[N],tot,dis[N],cnt[N];

IL void add(int x,int y,int z){edge[++tot].u=head[x];edge[tot].v=y;edge[tot].w=z;head[x]=tot;}

IL bool spfa(int now)

{

    rk vis[i]=false,dis[i]=2147483647,cnt[i]=false;

    queue<int>q;

    q.push(now);

    vis[now]=true;

    dis[now]=0;

    while(!q.empty())

    {

        int u=q.front();q.pop();vis[u]=false;

        if(cnt[u]>=n)return true;

        for(RI i=head[u];i;i=edge[i].u)

        {

            if(dis[edge[i].v]>dis[u]+edge[i].w)

            {

                dis[edge[i].v]=dis[u]+edge[i].w;

                if(!vis[edge[i].v])

                {

                    q.push(edge[i].v);

                    vis[edge[i].v]=true;

                    cnt[edge[i].v]++;

                    if(cnt[edge[i].v]>=n)return true;

                }

            }

        }

    }

    return false;

}

int main()

{

    read(T);

    while(T--)

    {

        read(n),read(m);

        tot=0;clear(head);

        for(RI i=1,u,v,w;i<=m;i++)

        {

            read(u),read(v),read(w);

            if(w<0)add(u,v,w);

            else add(u,v,w),add(v,u,w);

        }

        puts(spfa(1)?"YE5":"N0");

    }

}

T3最长公共子序列:

题目描述

给出1-n的两个排列P1和P2,求它们的最长公共子序列。

输入输出格式

输入格式:
 

第一行是一个数n,

接下来两行,每行为n个数,为自然数1-n的一个排列。

输出格式:
 

一个数,即最长公共子序列的长度

输入输出样例

输入样例#1: 复制

5

3 2 1 4 5

1 2 3 4 5

输出样例#1: 复制

3

说明

【数据规模】

对于50%的数据,n≤1000

对于100%的数据,n≤100000

#include<iostream>

#include<cstdio>

using namespace std;

int a[100001],b[100001],map[100001],f[100001];

int main()

{

    int n;

    cin>>n;

    for(int i=1;i<=n;i++){scanf("%d",&a[i]);map[a[i]]=i;}

    for(int i=1;i<=n;i++){scanf("%d",&b[i]);f[i]=0x7fffffff;}

    int len=0;

    f[0]=0;

    for(int i=1;i<=n;i++)

    {

        int l=0,r=len,mid;

        if(map[b[i]]>f[len])f[++len]=map[b[i]];

        else

        {

        while(l<r)

        {  

            mid=(l+r)/2;

            if(f[mid]>map[b[i]])r=mid;

            else l=mid+1;

        }

        f[l]=min(map[b[i]],f[l]);

        }

    }

    cout<<len;

    return 0;

}

T4借教室:

题目描述

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来nn天的借教室信息,其中第ii天学校有r_iri个教室可供租借。共有mm份订单,每份订单用三个正整数描述,分别为d_j,s_j,t_jdj,sj,tj,表示某租借者需要从第s_jsj天到第t_jtj天租借教室(包括第s_jsj天和第t_jtj天),每天需要租借d_jdj个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供d_jdj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第s_jsj天到第t_jtj天中有至少一天剩余的教室数量不足d_jdj个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

输入输出格式

输入格式:
 

第一行包含两个正整数n,mn,m,表示天数和订单的数量。

第二行包含nn个正整数,其中第ii个数为r_iri,表示第ii天可用于租借的教室数量。

接下来有mm行,每行包含三个正整数d_j,s_j,t_jdj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。

每行相邻的两个数之间均用一个空格隔开。天数与订单均用从11开始的整数编号。

输出格式:
 

如果所有订单均可满足,则输出只有一行,包含一个整数00。否则(订单无法完全满足)

输出两行,第一行输出一个负整数-1−1,第二行输出需要修改订单的申请人编号。

输入输出样例

输入样例#1: 复制

4 3

2 5 4 3

2 1 3

3 2 4

4 2 4

输出样例#1: 复制

-1

2

说明

【输入输出样例说明】

 11份订单满足后,44天剩余的教室数分别为 0,3,2,30,3,2,3。第 22 份订单要求第 22天到第 44 天每天提供33个教室,而第 33 天剩余的教室数为22,因此无法满足。分配停止,通知第22 个申请人修改订单。

【数据范围】

对于10%的数据,有1≤ n,m≤ 101≤n,m≤10

对于30%的数据,有1≤ n,m≤10001≤n,m≤1000

对于 70%的数据,有1 ≤ n,m ≤ 10^51≤n,m≤105

对于 100%的数据,有1 ≤ n,m ≤ 10^6,0 ≤ r_i,d_j≤ 10^9,1 ≤ s_j≤ t_j≤ n1≤n,m≤106,0≤ri,dj≤109,1≤sjtjn

NOIP 2012 提高组 第二天 第二题

// luogu-judger-enable-o2

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

struct Tree{

         int l,r;

         ll ma,add;

}t[8000040];

ll n,m,a[1000020],cho,x,y,k;

inline ll read(){

         ll k=0,f=1;

         char c=getchar();

         for(;!isdigit(c);c=getchar())

           if(c=='-')

             f=-1;

         for(;isdigit(c);c=getchar())

           k=k*10+c-'0';

         return k*f;

}

inline void build(int p,int l,int r){

         t[p].l=l,t[p].r=r;

         if(l==r){

           t[p].ma=a[l];

           return;

         }

         int mid=(l+r)/2;

         build(p*2,l,mid);

         build(p*2+1,mid+1,r);

         t[p].ma=min(t[p*2].ma,t[p*2+1].ma);

         return ;

}

inline void spread(int p){

         if(t[p].add){

                   t[p*2].ma+=t[p].add;

                   t[p*2].add+=t[p].add;

                   t[p*2+1].ma+=t[p].add;

                   t[p*2+1].add+=t[p].add;

                   t[p].add=0;

         }

         return;

}

inline void change(int p){

         if(x<=t[p].l&&y>=t[p].r){

                   t[p].ma+=k;

                   t[p].add+=k;

                   return;

         }

         spread(p);

         int mid=(t[p].l+t[p].r)/2;

         if(x<=mid)

           change(p*2);

         if(mid<y)

           change(p*2+1);

         t[p].ma=min(t[p*2].ma,t[p*2+1].ma);

         return;

}

inline ll ask(int p){

         ll num=1e11;

         spread(p);

         if(t[p].l>=x&&t[p].r<=y)

                   return t[p].ma;

         int mid=(t[p].l+t[p].r)/2;

         if(mid>=x)

           num=min(ask(p*2),num);

         if(mid<y)

           num=min(ask(p*2+1),num);

         return num;

}

int main(){

         n=read(),m=read();

         for(int i=1;i<=n;i++)

           a[i]=read();

         build(1,1,n);

         for(int i=1;i<=m;i++){

                   k=-read(),x=read(),y=read();

                   change(1);

                   ll ans=ask(1);

                   if(ans<0){

                       printf("-1\n%d",i);

                            return 0;

                   }

         }

         cout<<0;

         return 0;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值