模板整理

一、数论

1.1排序

1.1.1快速排序

输入:
10
6 1 2 7 9 3 4 5 10 8
输出:
1 2 3 4 5 6 7 8 9 10

/*
快速排序
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[100];
int n;
void quicksort(int L,int R)
{
    if(L>R)
        return ;
    int temp=a[L];//temp中存的是基准数
    int i=L;
    int j=R;
    while(i!=j)
    {
        while(a[j]>=temp&&i<j)//因为基准数在左边,所以每次必须是哨兵j先出发(特别注意)
            j--;
        while(a[i]<=temp&&i<j)
            i++;
        if(i<j)//当哨兵i和哨兵j没有相遇的时候
            swap(a[i],a[j]);//交换两个数在数组中的位置

    }
    swap(a[i],a[L]);//最终将基准数归位
    quicksort(L,i-1);
    quicksort(i+1,R);
    return;
}
int main()
{
    while(~scanf("%d",&n))
    {
        memset(a,0,sizeof(a));
        //输入
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        quicksort(1,n);
        //输出
        printf("%d",a[1]);
        for(int i=2; i<=n; i++)
            printf(" %d",a[i]);
        printf("\n");
    }
    return 0;
}

1.1.2归并排序

归并排序可以用来求逆序对的个数。
逆序对:
对于序列:3 1 8 7 2
有 3和1,3和2,8和7,8和2,7和2,总共5对逆序对。

/*
归并排序
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[110];
int temp[110];
int n;
int num;//求逆序对的个数
void mergesort(int L,int R)
{
    if(L==R)
        return;
    int mid=(L+R)>>1;
    mergesort(L,mid);
    mergesort(mid+1,R);
    int i=L;
    int j=mid+1;
    int r=L;
    while(i<=mid&&j<=R)
    {
        if(a[i]<=a[j])
        {
           temp[r++]=a[i++];
        }
        else
        {
            temp[r++]=a[j++];
            //num+=(mid-i+1);左边元素的个数
        }
    }
    while(i<=mid)
    {
        temp[r++]=a[i++];
    }
    while(j<=R)
    {
        temp[r++]=a[j++];
    }
    for(int s=L;s<=R;s++)
        a[s]=temp[s];
}
int main()
{
    while(~scanf("%d",&n))
    {
        memset(a,0,sizeof(a));
        memset(temp,0,sizeof(temp));
        num=0;

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

        //printf("%d\n",num);
        printf("%d",a[1]);
        for(int i=2; i<=n; i++)
            printf(" %d",a[i]);
        printf("\n");
    }
    return 0;
}


1.2 快速幂

1.2.1 人见人爱A^B HDU - 2035

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
ll quick_pow(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b%2!=0)
            res=res*a%1000;//保留几位对10的几次方取余
        a=a*a%1000;
        b/=2;
    }
    return res;
}
int main()
{
    ll a,b;
    while(scanf("%lld%lld",&a,&b)&&(a||b))
    {
        ll ans=quick_pow(a,b);
        printf("%lld\n",ans);
    }
    return 0;
}

1.3 素数打表

1.3.1 埃氏筛法

bool book[10000010];//0为素数  1不是
int su[1000100];//存放素数
int r;//素数的个数
void prime()
{
    r=0;
    memset(book,0,sizeof(book));
    book[0]=book[1]=1;
    for(int i=2;i<=10000000;i++)
    {
        if(!book[i])
        {
            su[r++]=i;
            for(int j=i*2;j<=10000000;j+=i)
                book[j]=1;
        }
    }
}

1.3.2 欧拉筛法

bool book[10000010];//0为素数  1不是
int su[1000100];//存放素数
int r;//素数的个数
void prime()
{
    r=0;
    memset(book,0,sizeof(book));
    book[0]=book[1]=1;
    for(int i=2; i<=10000000; i++)
    {
        if(book[i]==0)
            su[r++]=i;
        for(int j=0; j<r&&i*su[j]<=10000000; j++)
        {
            book[i*su[j]]=1;
            if(i%su[j]==0)
                break;
//i%su[j]==0此处是重点,避免了很多的重复判断,
//比如i=9,现在素数是2,3,5,7,进入二重循环,visit[2*9]=1;visit[3*9]=1;
//这个时候9%3==0,要跳出。因为5*9可以用3*15来代替,
// 如果这个时候计算了,i=15的时候又会被重复计算一次
// ,所以这里大量避免了重复运算。
        }
    }
}

1.4 欧几里德算法

/*
辗转相除法
*/
#include<stdio.h>
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    int ans=gcd(a,b);
    printf("%d\n",ans);
    return 0;
}

1.5 区间筛法

1.5.1 Help Hanzo LightOJ - 1197

/*
题目大意:给出一个区间,求这个区间的素数个数。

思路:因为范围值大,所以需要用区间筛法。

b以内的合数最小质因数一定不超过sqrt(b);
如果有sqrt(b)以内的素数表的话,
就可以把埃氏筛法运用在[a,b)上了。
也就是说,先分别做好[a,sqrt(b))的表和[a,b)的表,
然后从[a,sqrt(b))的表中删除的同时,
也将倍数从[a,b)的表中划去,
最后剩下的就是区间[a,b)内的素数了。
*/
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define ll long long
ll sum;
bool su[1000010];
bool book[1000010];
void solve(ll a,ll b)
{
    for(ll i=0; i*i<b; i++)//sqrt(b)
        su[i]=0;
    for(ll i=0; i<b-a; i++)//[a,b)
        book[i]=0;
    for(ll i=2; i*i<b; i++)
    {
        if(su[i]==0)
        {
            for(ll j=i*2; j*j<b; j+=i)//sqrt(b)
                su[j]=1;
            for(ll k=max(2ll,(a+i-1)/i)*i; k<b; k+=i)//[a,b)
                book[k-a]=1;//2ll是2的长整型
                //((a+i-1)/i)*i是满足>=a&&%i==0的离a最近的数
        }
    }
    for(int i=0; i<b-a; i++)
    {
        if(book[i]==0)
            sum++;
    }
}
int main()
{
    int t;
    int o=1;
    scanf("%d",&t);
    while(t--)
    {
        ll a,b;
        scanf("%lld%lld",&a,&b);
        sum=0;
        solve(a,b+1);
        if(a==1)
            sum--;
        printf("Case %d: %lld\n",o++,sum);
    }
    return 0;
}

1.6 前三位和后三位

/*
Leading and Trailing LightOJ - 1282 
题目大意:对于给的数一个啊,一个b,求a的b次方的前三位数,和后三位数。

思路:对于后三位直接使用快速幂运算,对1000取余即可,
对于前三位,任意数都可以用10的w次方进行表示。详细看代码。
*/
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define ll long long
ll mod_pow(ll x,ll n)//快速幂
{
    ll res=1;
    while(n>0)
    {
        if(n&1)
            res=res*x%1000;
        x=x*x%1000;
        n>>=1;
    }
    return res;
}
int main()
{
    int o=1;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll n,k;
        scanf("%lld%lld",&n,&k);
        ll ans1,ans2;
        double w;
        w=k*log10(n);//10的w次方==n的k次方,两边同时取log10
        w=w-(ll)w;//取w的小数部分
        ans1=(ll)pow(10,w+2);//10的w次方,w<0,所得的数应该>=1,所以再乘100
        ans2=mod_pow(n,k);//后三位
        printf("Case %d: %lld %03lld\n",o++,ans1,ans2);
    }
    return 0;
}

二、常用技巧

2.1尺取法

2.1.1 Subsequence (POJ No.3061)

方法一:预先计算好前缀和sum,然后使用二分搜索快速确定使序列和不小于S的的结尾的t的最小值。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[100010];
int sum[100010];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(a,0,sizeof(a));
        memset(sum,0,sizeof(sum));
        int n,s;
        scanf("%d%d",&n,&s);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        for(int i=1; i<=n; i++)
            sum[i]=sum[i-1]+a[i];
        if(sum[n]<s)
            printf("0\n");
        else
        {
            int res=n;
            for(int i=0; sum[i]+s<=sum[n]; i++)
            {
                int t=lower_bound(sum+i,sum+n,sum[i]+s)-sum;
                res=min(res,t-i);
            }
            printf("%d\n",res);
        }
    }
    return 0;
}

方法二:尺取法

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[100010];
int sum[100010];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(a,0,sizeof(a));
        memset(sum,0,sizeof(sum));
        int n,s;
        scanf("%d%d",&n,&s);
        for(int i=0; i<n; i++)
            scanf("%d",&a[i]);
        int res=n+1;
        int i=0,j=0;
        int sum=0;
        while(1)
        {
            while(i<n&&sum<s)
                sum+=a[i++];
            if(sum<s)
                break;
            res=min(res,i-j);
            sum-=a[j++];
        }
        if(res>n)
            printf("0\n");
        else
            printf("%d\n",res);
    }
    return 0;
}

2.1 二分

2.1.1 整数定义域上的二分

/*
整数定义域上的二分
*/
int erfen()
{
    int L=1;
    int R=n;
    int ans;
    while(L<=R)
    {
        int mid=(L+R)>>1;
        if(check(mid))
        {
            ans=mid;
            L=mid+1;
        }
        else
            R=mid-1;
    }
    return ans;
}

2.1.2 实数域上的二分

/*
实数域上的二分
*/
double erfen(double L,double R)
{
    double mid;
    while(fabs(L-R)>dlt)//dlt=0.001(根据题目要求确定精度)
    {
        mid=(L+R)/2.0;
        if(check(mid))
            R=mid;
        else
            L=mid;
    }
    return L;
}

3.1 三分

double L=0,R=1e9;
while(R-L>=1e-3)
{
    double Lmid=L+(R-L)/3;
    double Rmid=R-(R-L)/3;
    if(f(Lmid)<f(Rmid))
        L=Lmid;
    else
        R=Rmid;
}

三、字符串

3.1 KMP算法

3.1.1 剪花布条 HDU - 2087

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char s1[1010];
char s2[1010];
int nex[1010];
int L1,L2;
void getf()//得到next数组
{
    memset(nex,0,sizeof(nex));
    int i=0,j=-1;
    nex[0]=-1;
    while(i<L2)
    {
        if(j<0||s2[i]==s2[j])
            nex[++i]=++j;
        else
            j=nex[j];
    }
}
int main()
{
    while(~scanf("%s",s1))
    {
        if(strcmp(s1,"#")==0)
            break;
        scanf("%s",s2);
        L1=strlen(s1);
        L2=strlen(s2);
        getf();
        int j=0;
        int ans=0;
        for(int i=0; i<L1; i++)
        {
            while(j>0&&s1[i]!=s2[j])//j不为0且当前串不匹配
                j=nex[j];
            if(s1[i]==s2[j])
                j++;
            if(j==L2)
            {
                ans++;
                //j=nex[j];
                j=0;//匹配串不能重叠
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

3.1.2 Power Strings POJ - 2406

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char s[1000010];
int nex[1000010];
int L;
void getf()
{
    memset(nex,0,sizeof(nex));
    int i=0,j=-1;
    nex[0]=-1;
    while(i<L)
    {
        if(j<0||s[i]==s[j])
            nex[++i]=++j;
        else
            j=nex[j];
    }
}
int main()
{
    while(1)
    {
        scanf("%s",s);
        if(strcmp(s,".")==0)
            break;
        L=strlen(s);
        getf();
        if(L%(L-nex[L])==0)//循环节为L-next[L]
            printf("%d\n",L/(L-nex[L]));//循环次数L/(L-nex[L])
        else
            printf("1\n");
    }
    return 0;
}

3.2 Manacher算法

3.2.1 最长回文 HDU - 3068

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char a[110010];
char s[210100];
int p[210030];
int r;
void manacher()
{
    memset(p,0,sizeof(p));
    int l=strlen(a);
    r=2;
    s[0]='@';
    s[1]='#';
    for(int i=0; i<l; i++)
    {
        s[r++]=a[i];
        s[r++]='#';
    }
    int mx=0,id=0;
    for(int i=1; i<r; i++)
    {
        p[i]=mx>i?min(mx-i,p[2*id-i]):1;
        while(s[i+p[i]]==s[i-p[i]])
            p[i]++;
        if(i+p[i]>mx)
        {
            mx=i+p[i];
            id=i;
        }
    }
}
int main()
{
    while(~scanf("%s",a))
    {
        manacher();
        int ans=0;
        for(int i=1; i<r; i++)
            ans=max(ans,p[i]-1);
        printf("%d\n",ans);
    }
    return 0;
}

四、动态规划

4.1 最长公共子序列

/*
最长公共子序列
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int L1,L2;//输入的数组长度
char s1[1010];
char s2[1010];
int dp[1010][1010];
int main()
{
    scanf("%s",s1);
    scanf("%s",s2);
    L1=strlen(s1);
    L2=strlen(s2);
    for(int i=0;i<L1;i++)
    {
        for(int j=0;j<L2;j++)
        {
            if(s1[i]==s2[j])
            {
                dp[i+1][j+1]=dp[i][j]+1;
            }
            else
            {
                dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
            }
        }
    }
    printf("%d\n",dp[L1][L2]);
    return 0;
}

4.2 最长上升子序列

4.2.1 方法一:动态规划

/*
最长上升子序列
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n;
int a[1010];
int dp[1010];
int main()
{
    scanf("%d",&n);
    for(int i=0; i<n; i++)
        scanf("%d",&a[i]);
    int ans=0;
    for(int i=0; i<n; i++)
    {
        dp[i]=1;
        for(int j=0; j<i; j++)
        {
            if(a[j]<a[i])
                dp[i]=max(dp[i],dp[j]+1);
        }
        ans=max(ans,dp[i]);
    }
    printf("%d\n",ans);
    return 0;
}

4.2.2 方法二:贪心+二分

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
int n;
int a[1010];
int dp[1010];
int main()
{
    scanf("%d",&n);
    for(int i=0; i<n; i++)
        scanf("%d",&a[i]);

    //最长上升子序列
    fill(dp,dp+n,inf);
    for(int i=0; i<n; i++)
    {
        *lower_bound(dp,dp+n,a[i])=a[i];
    }
    printf("%d\n",lower_bound(dp,dp+n,inf)-dp);

    return 0;
}

4.3 背包问题

4.3.1 0-1背包

Bone Collector HDU - 2602

方法一:一维数组

/*
01背包
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[1010],w[1010];//价值、重量(体积)
int dp[1010];
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);//物品的数量n和背包的容量m
    for(int i=1;i<=n;i++)
        scanf("%d",&v[i]);
    for(int i=1;i<=n;i++)
        scanf("%d",&w[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=w[i];j--)//逆序***
        {
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        }
    }
    printf("%d\n",dp[m]);
    return 0;
}

方法二:二维数组

/*
01背包
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[1010],w[1010];//价值、重量(体积)
int dp[1010][1010];
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);//物品的数量n和背包的容量m
    for(int i=1; i<=n; i++)
        scanf("%d",&v[i]);
    for(int i=1; i<=n; i++)
        scanf("%d",&w[i]);
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=m; j++)
        {
            dp[i][j]=dp[i-1][j];
        }
        for(int k=w[i]; k<=m; k++)
            dp[i][k]=max(dp[i-1][k],dp[i-1][k-w[i]]+v[i]);
    }
    printf("%d\n",dp[n][m]);
    return 0;
}

4.3.2 完全背包

湫湫系列故事——减肥记I HDU - 4508

/*
完全背包
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[1010],w[1010];//价值、重量(体积)
int dp[1010];
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);//物品的数量n和背包的容量m
    for(int i=1; i<=n; i++)
        scanf("%d",&v[i]);
    for(int i=1; i<=n; i++)
        scanf("%d",&w[i]);
    for(int i=1; i<=n; i++)
    {
        for(int j=w[i];j<=m;j++)//与01背包唯一不同
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    }
    printf("%d\n",dp[m]);
    return 0;
}

4.3.3 多重背包

悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 HDU - 2191

/*
多重背包
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[1010],w[1010];//价值、重量(体积)
int num[1010];//每种物品的数量
int dp[1010];
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);//物品的数量n和背包的容量m
    for(int i=1; i<=n; i++)
        scanf("%d",&v[i]);
    for(int i=1; i<=n; i++)
        scanf("%d",&w[i]);
    for(int i=1;i<=n;i++)
        scanf("%d",&num[i]);
    
    for(int i=1; i<=n; i++)
    {
        for(int j=m;j>=0;j--)
        {
            for(int k=0;k<=num[i];k++)
            {
                if(j-k*v[i]<0)
                    break;
                dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
            }
        }
    }
    printf("%d\n",dp[m]);
    return 0;
}

五、数据结构

5.1线段树

5.1.1 更新一段区间,求区间和

/*
A Simple Problem with Integers POJ - 3468
Just a Hook HDU - 1698 
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
long long sum[500000],lazy[500000];
void creat(int l,int r,int o)//建立线段树
{
    if(l==r)
    {
        scanf("%lld",&sum[o]);
        return;
    }
    int mid=(l+r)>>1;
    creat(l,mid,o<<1);
    creat(mid+1,r,o<<1|1);
    sum[o]=sum[o<<1]+sum[o<<1|1];
}
void push(int o,int l)//更新左儿子和右儿子,下移操作
{
    if(lazy[o])
    {
        lazy[o<<1]+=lazy[o];
        lazy[o<<1|1]+=lazy[o];
        sum[o<<1]+=lazy[o]*(l-(l>>1));
        sum[o<<1|1]+=lazy[o]*(l>>1);
        lazy[o]=0;//**********
    }
}
void update(int x,int y,int l,int r,int o,int c)
{
    if(l>=x&&r<=y)//****x和y为所要的区间。l-r为访问的区间
    {
        sum[o]+=c*(r-l+1);
        lazy[o]+=c;
        return;
    }
    push(o,r-l+1);
    int mid=(r+l)>>1;
    if(mid>=x)
        update(x,y,l,mid,o<<1,c);
    if(y>mid)
        update(x,y,mid+1,r,o<<1|1,c);
    sum[o]=sum[o<<1]+sum[o<<1|1];
}
long long ask(int x,int y,int l,int r,int o)
{
    if(l>=x&&r<=y)
        return sum[o];
    push(o,r-l+1);
    int mid=(r+l)>>1;
    long long num=0;
    if(mid>=x)
        num+=ask(x,y,l,mid,o<<1);
    if(y>mid)
        num+=ask(x,y,mid+1,r,o<<1|1);
    return num;
}
int main()
{
    int n,q;
    while(~scanf("%d%d",&n,&q))
    {
        memset(sum,0,sizeof(sum));
        memset(lazy,0,sizeof(lazy));
        creat(1,n,1);
        while(q--)
        {
            char a[10];
            int l,r,c;
            scanf("%s",a);
            if(a[0]=='Q')
            {
                scanf("%d%d",&l,&r);
                printf("%lld\n",ask(l,r,1,n,1));
            }
            else
            {
                scanf("%d%d%d",&l,&r,&c);
                update(l,r,1,n,1,c);
            }
        }
    }
    return 0;
}

5.1.2 求区间最值的差值

/*
Balanced Lineup POJ - 3264
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int ma[220000],mi[220000];
void creat(int l,int r,int o)//建立树状树
{
    if(l==r)
    {
        scanf("%d",&ma[o]);
        mi[o]=ma[o];
        return ;
    }
    int mid=(r+l)>>1;
    creat(l,mid,o<<1);
    creat(mid+1,r,o<<1|1);
    ma[o]=max(ma[o<<1],ma[o<<1|1]);
    mi[o]=min(mi[o<<1],mi[o<<1|1]);
}
int ask1(int x,int y,int l,int r,int o)//最大值
{
    if(l>=x&&r<=y)
        return ma[o];
    int mid=(l+r)>>1;
    int t1=0,t2=0;
    if(x<=mid)
        t1=ask1(x,y,l,mid,o<<1);
    if(y>mid)
        t2=ask1(x,y,mid+1,r,o<<1|1);
    return max(t1,t2);
}
int ask2(int x,int y,int l,int r,int o)//最小值
{
    if(l>=x&&r<=y)
        return mi[o];
    int mid=(l+r)>>1;
    int t1=0x3f3f3f3f,t2=0x3f3f3f3f;
    if(x<=mid)
        t1=ask2(x,y,l,mid,o<<1);
    if(y>mid)
        t2=ask2(x,y,mid+1,r,o<<1|1);
    return min(t1,t2);
}
int main()
{
    int n,m;
    while(~scanf("%d",&n))
    {
        scanf("%d",&m);
        memset(ma,0,sizeof(ma));
        memset(mi,0,sizeof(mi));
        creat(1,n,1);
        while(m--)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            int t1=ask1(a,b,1,n,1);
            int t2=ask2(a,b,1,n,1);
            printf("%d\n",t1-t2);
        }
    }
    return 0;
}

5.1.3 更新某点,求区间和

/*
敌兵布阵 HDU - 1166
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
long long sum[220000];
void creat(int l,int r,int o)//建立线段树
{
    if(l==r)
    {
        scanf("%lld",&sum[o]);
        return;
    }
    int mid=(l+r)>>1;
    creat(l,mid,o<<1);
    creat(mid+1,r,o<<1|1);
    sum[o]=sum[o<<1]+sum[o<<1|1];
}
void update(int x,int l,int r,int o,int c)//更改某个点,一个线段二分的思想
{
    if(l>r)return;
    if(l==r)
    {
        sum[o]+=c;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=x)
        update(x,l,mid,o<<1,c);
    else
        update(x,mid+1,r,o<<1|1,c);
    sum[o]=sum[o<<1]+sum[o<<1|1];
}
long long ask(int x,int y,int l,int r,int o)//查找
{
    if(l>=x&&r<=y)
        return sum[o];
    int mid=(l+r)>>1;
    long long num=0;
    if(mid>=x)
        num+=ask(x,y,l,mid,o<<1);
    if(y>mid)
        num+=ask(x,y,mid+1,r,o<<1|1);
    return num;
}
int main()
{
    int t,o=1;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        memset(sum,0,sizeof(sum));
        creat(1,n,1);
        char a[10];
        printf("Case %d:\n",o++);
        while(1)
        {
            scanf("%s",a);
            if(strcmp(a,"End")==0)
                break;
            int l,r;
            int x;
            int c;
            if(a[0]=='Q')
            {
                scanf("%d%d",&l,&r);
                printf("%lld\n",ask(l,r,1,n,1));
            }
            if(a[0]=='A')
            {
                scanf("%d%d",&x,&c);
                update(x,1,n,1,c);
            }
            if(a[0]=='S')
            {
                scanf("%d%d",&x,&c);
                update(x,1,n,1,-c);
            }
        }
    }
    return 0;
}

5.1.4 区间加,区间乘,求区间和

/*
维护序列 LibreOJ - 10129
*/
#include<bits/stdc++.h>
#define ll long long
#define N 400010
using namespace std;
ll mod,Sum[N],add[N],mul[N];
void init()
{
    for(int i=1;i<N;i++)
        add[i]=0,mul[i]=1;
}
void build(int l,int r,int o)
{
    if(l==r)
    {
        scanf("%lld",&Sum[o]);
        Sum[o]%=mod;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,o<<1);
    build(mid+1,r,o<<1|1);
    Sum[o]=(Sum[o<<1]+Sum[o<<1|1])%mod;
}
void pushdown(int o,int len)
{
    if(add[o]||mul[o]!=1)
    {
        int lson=o<<1;
        int rson=o<<1|1;
 
        Sum[lson]=Sum[lson]*mul[o]%mod;
        Sum[rson]=Sum[rson]*mul[o]%mod;
 
        Sum[lson]=(Sum[lson]+add[o]*(len-(len>>1)))%mod;
        Sum[rson]=(Sum[rson]+add[o]*(len>>1))%mod;
 
        mul[lson]=mul[lson]*mul[o]%mod;
        mul[rson]=mul[rson]*mul[o]%mod;
 
        add[lson]=add[lson]*mul[o]%mod;
        add[rson]=add[rson]*mul[o]%mod;
 
        add[lson]=(add[lson]+add[o])%mod;
        add[rson]=(add[rson]+add[o])%mod;
 
        add[o]=0,mul[o]=1;
    }
}
void update(int x,int y,int l,int r,int o,ll c,int flag)
{
    if(l>=x&&r<=y)
    {
        if(flag)
        {
           Sum[o]=Sum[o]*c%mod;
           add[o]=add[o]*c%mod;
           mul[o]=mul[o]*c%mod;
        }
        else
        {
            Sum[o]=(Sum[o]+c*(r-l+1))%mod;
            add[o]=(add[o]+c)%mod;
        }
        return;
    }
    pushdown(o,r-l+1);
    int mid=(l+r)>>1;
    if(x<=mid)update(x,y,l,mid,o<<1,c,flag);
    if(y>mid)update(x,y,mid+1,r,o<<1|1,c,flag);
    Sum[o]=(Sum[o<<1]+Sum[o<<1|1])%mod;
}
ll query(int x,int y,int l,int r,int o)
{
    if(l>=x&&r<=y)
        return Sum[o];
    pushdown(o,r-l+1);
    int mid=(l+r)>>1;
    ll sum=0;
    if(x<=mid)sum=(sum+query(x,y,l,mid,o<<1))%mod;
    if(y>mid)sum=(sum+query(x,y,mid+1,r,o<<1|1))%mod;
    return sum;
}
int main()
{
    init();
    int n;
    scanf("%d%lld",&n,&mod);
    build(1,n,1);
    int m;
    scanf("%d",&m);
    while(m--)
    {
        int op,l,r;
        ll c;
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d%lld",&l,&r,&c);
            update(l,r,1,n,1,c,1);
        }
        else if(op==2)
        {
            scanf("%d%d%lld",&l,&r,&c);
            update(l,r,1,n,1,c,0);
        }
        else
        {
            scanf("%d%d",&l,&r);
            printf("%lld\n",query(l,r,1,n,1));
        }
    }
    return 0;
}

5.2 树状数组

/*
lowbit(x)表示取出非负整数x在二进制表示下:
最低位的1以及它后面的0构成的数值
*/
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int v)//在序列第x个位置上加上v,并在树状数组中修改相应元素
{
    while(x<=n)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
}
int sum(int x)//计算序列中第1数到第x数的和
{
    int res=0;
    while(x>0)
    {
        res+=c[x];
        x-=lowbit(x);
    }
    return res;
}

5.3 二叉搜索树

5.3.1 判断是否为同一棵树

/*
二叉搜索树 HDU - 3791 
题目大意:给的第一组数据建成标准树,
下面的n组数据让判断是否和标准树是同一棵树。

思路: 如果两个是同一棵树的话,它们的节点应该是相同的。
所以采用一种遍历方式,依次判断它们的节点是否相同。
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int flag;
struct node
{
    int data;
    node *lson;
    node *rson;
//    node()
//    {
//        lson=NULL;
//        rson=NULL:
//    }
};
node *inser(node *root,int x)
{
    if(root==NULL)
    {
        node *p=new node;//申请新空间
        p->data=x;//赋值
        p->lson=p->rson=NULL;
        return p;
    }
    else
    {
        if(root->data>x)//左子树
            root->lson=inser(root->lson,x);
        if(root->data<x)//右子树
            root->rson=inser(root->rson,x);
        return root;
    }
}
void check(node *root,node *room)
{
    if(root!=NULL&&room!=NULL)
    {
        //前序遍历,根-左-右
        //中序遍历,左-根-右
        //后序遍历,左-右-根
        if(root->data!=room->data)
            flag=0;
        check(root->lson,room->lson);
        check(root->rson,room->rson);
    }
    else if(root!=NULL||room!=NULL)//所在位置,一个有值,一个没有
        flag=0;
}
int main()
{
    int n;
    while(~scanf("%d",&n)&&n)
    {
        char a[20];
        char b[20];
        node *root=NULL;//地址为空
        scanf("%s",a);
        for(int i=0; i<strlen(a); i++)
            root=inser(root,a[i]-'0');//构建一棵树
        for(int i=0; i<n; i++)
        {
            flag=1;
            node *room=NULL;
            scanf("%s",b);
            for(int i=0; i<strlen(b); i++)
                room=inser(room,b[i]-'0');
            check(root,room);
            if(flag)
                printf("YES\n");
            else
                printf("NO\n");
        }
    }
    return 0;
}

5.3.2 知前序和中序求后序

/*
Tree Recovery POJ - 2255 
前序的遍历方式:根->左->右,中序的遍历为左->根->右,
在前序中从前向后进行遍历,在中序中找到根在的位置,
从而判断当前的左右子树,递归的过程完成左子树,然后回溯的过程完成右子树。
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct node
{
    char data;
    node *lson;
    node *rson;
    node()
    {
        lson=NULL;
        rson=NULL;
    }
};
node *inser(char *s1,char *s2,int l)
{
    if(l<=0)
        return NULL;
    node *root=new node();
    root->data=*s1;
    char *p;
    for(p=s2; p!=NULL; p++)//在中序遍历结果中找到 当前根,此时在中序序列中此根左边是左子树,右边是右子树
    {
        if(*p==*s1)
            break;
    }
    int kk=p-s2;//左子树的长度
    root->lson=inser(s1+1,s2,kk);//访问左子树
    root->rson=inser(s1+kk+1,s2+kk+1,l-kk-1);//访问右子树
    return root;
 
}
void out(node *root)
{
    if(root==NULL)
        return ;
    out(root->lson);
    out(root->rson);
    printf("%c",root->data);
}
int main()
{
    char a[10010],b[10010];
    while(~scanf("%s%s",a,b))
    {
        int l=strlen(a);
        node *root=new node();
        root=inser(a,b,l);
        out(root);
        printf("\n");
    }
    return 0;
}

5.3.3 输出前序遍历

/*
Tree Recovery POJ - 2255 

题目大意:已知一棵树,求一棵树和现在的树相同,但字典序最小。

思路: 二叉搜索树需要满足左儿子小于父节点,右儿子大于父节点,
要求是同一棵树,所以应该根节点相同,先根后左最后右,即前序遍历。
*/
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
vector<int>V;
struct node
{
    int data;
    node *lson;//左儿子
    node *rson;//右儿子
    node()//初始化
    {
        lson=NULL;
        rson=NULL;
    }
};
void inser(node *&root,int x)//BST插入函数
{
    if(root==NULL)//遇到为NULL,即应该插入的位置
    {
        root=new node();//新建节点
        root->data=x;//赋权值
        return ;
    }
    if(root->data==x)//当前节点为插入的值,直接返回
        return;
    else if(root->data>x)//插入的值小于当前节点,需插入左子树
        inser(root->lson,x);
    else if(root->data<x)//插入的值大于当前节点,需插入右子树
        inser(root->rson,x);
}
node *build()//BST建树********
{
    node *root=NULL;
    vector<int>::iterator it;//迭代器
    for(it=V.begin(); it!=V.end(); it++)//依次插入树中
    {
        inser(root,*it);
    }
    return root;//返回的是根节点
}
void out(node *root,node *flag)//输出函数
{
    if(root==NULL)//当访问的为空,即返回(递归的边界)
        return ;
    if(root!=flag)
        printf(" ");
    printf("%d",root->data);//输出当前节点的值
    out(root->lson,flag);//访问左子树
    out(root->rson,flag);//访问右子树
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int x;
        V.clear();//容器的清空
        for(int i=0; i<n; i++)
        {
            scanf("%d",&x);
            V.push_back(x);
        }
        node *root=build();
        out(root,root);
        printf("\n");
    }
    return 0;
}

5.3.4 根节点到指定节点的路径

/*
Elven Postman HDU - 5444 
题目大意:已知一棵二叉树(倒置),
求从根节点到所要访问的节点的路径。

思路:正着建二叉树,在建树的同时将路径记录下来,
倒置后,左右方向相反。
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct node
{
    int data;
    node *lson;
    node *rson;
    node()
    {
        lson=rson=NULL;
    }
};
node *inser(node *root,int x)
{
    if(root==NULL)//****************
    {
        node *p=new node;
        p->data=x;
        return p;
    }
    if(x<root->data)
        root->lson=inser(root->lson,x);
    if(x>root->data)
        root->rson=inser(root->rson,x);
    return root;
}
char ans[12000];
int flag;
void find(node *root,int x)
{
    if(root==NULL)
        return;
    if(root->data==x)
        return ;
    if(root->data>x)//左
    {
        ans[flag++]='E';
        find(root->lson,x);
    }
    if(root->data<x)//右
    {
        ans[flag++]='W';
        find(root->rson,x);
    }
}
int main()
{
    int c;
    scanf("%d",&c);
    while(c--)
    {
        int m,n;
        scanf("%d",&n);
        node *root=NULL;
        int x;
        for(int i=0; i<n; i++)
        {
            scanf("%d",&x);
            root=inser(root,x);
        }
        scanf("%d",&m);
        for(int i=0; i<m; i++)
        {
            flag=0;
            scanf("%d",&x);
            find(root,x);
            if(!flag)
                printf("\n");
            else
            {
                for(int i=0;i<flag;i++)
                    printf("%c",ans[i]);
                printf("\n");
            }
        }
    }
    return 0;
}

六、图论

6.1 邻接表

/*
邻接表
u v w 三个数组来记录每条边的信息
u[i] v[i] w[i] 表示第i条边是从u[i]号顶点到v[i]号顶点,且权值为w[i]
first[u[i]]保存顶点u[i]的第一条边的编号
next[i]存储 "编号为i的边" 的 "下一条边" 的编号
*/
memset(first,-1,sizeof(first));
for(int i=1; i<=m; i++)
{
    scanf("%d%d%d",&u[i],&v[i],&w[i]);
    next[i]=first[u[i]];
    first[u[i]]=i;
}

6.2 最短路

6.2.1 Floyd

/*
Floyd
有n个点
*/
void Floyd()
{
    //初始化
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j)
                e[i][j]=0;
            else
                e[i][j]=0x3f3f3f3f;
        }
    }
    //五行代码
    for(int k=1; k<=n; k++)
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if(e[i][j]>e[i][k]+e[k][j])
                {
                    e[i][j]=e[i][k]+e[k][j];
                }
            }
        }
    }
}

6.2.2 dijkstra

/*
dijkstra
有n个点
e数组用来存图
dis数组用来存1号点到各点的距离
book数组用来标记点是否走过
*/
void dijkstra()
{
    //初始化
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j)
                e[i][j]=0;
            else
                e[i][j]=0x3f3f3f3f;
        }
    }
    //1号点到各点的距离
    for(int i=1;i<=n;i++)
    {
        dis[i]=e[1][i];
    }
    memset(book,0,sizeof(book));
    book[1]=1;
    
    //dijkstra算法核心语句
    int mi,u;
    for(int i=1;i<=n-1;i++)
    {
        mi=0x3f3f3f3f;
        for(int j=1;j<=n;j++)
        {
            if(!book[j]&&dis[j]<mi)
            {
                mi=dis[j];
                u=j;
            }
        }
        book[u]=1;
        for(int v=1;v<=n;v++)
        {
            if(dis[v]>dis[u]+e[u][v])
            {
                dis[v]=dis[u]+e[u][v];
            }
        }
    }
}

6.2.3 Bellman-Ford 解决负权边

/*
Bellman-Ford
dis[v[i]]为1到v[i]的最短距离
dis[u[i]]为1到u[i]的最短距离
w[i]为u[i]到v[i]的距离
*/
void Bellman()
{
    //初始化dis数组,这里是1号点到其余各点的初始路程
    for(int i=1;i<=n;i++)
    {
        dis[i]=inf;
    }
    dis[1]=0;
    //Bellman-Ford算法核心语句
    for(int k=1;k<=n-1;k++)
    {
        int check=0;//用来标记在本轮松弛中数组dis是否会发生更新
        //进行一轮松弛
        for(int i=1;i<=m;i++)
        {
            if(dis[v[i]]>dis[u[i]]+w[i])
            {
                dis[v[i]]=dis[u[i]]+w[i];
                check=1;
            }
        }
        //松弛完毕后检测数组dis是否有更新
        if(check==0)
            break;//如果数组dis没有更新,提前结束算法
    }
    //检测负权回路
    flag=0;
    for(int i=1;i<m;i++)
    {
        if(dis[v[i]]>dis[u[i]]+w[i])
        {
            flag=1;
        }
    }
    if(flag==1)
        printf("此图含有负权回路\n");
}

6.2.4 SPFA(队列优化的Bellman-Ford)

/*
SPFA

book[now]=0;//出队
需要出队的样例
4 5
1 2 18
1 4 10
2 3 4
3 4 3
4 2 2

*/
void SPFA()
{
    //初始化
    memset(book,0,sizeof(book));
    for(int i=1; i<=n; i++)
        dis[i]=inf;//1号点到其余各点的路程
    dis[1]=0;
    book[1]=1;//标记1号点已经入队
    
    queue<int>q;
    int now;
    q.push(1);
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        for(int k=first[now]; k!=-1; k=next[k])
        {
            if(dis[v[k]]>dis[u[k]]+w[k])//判断是否松弛成功
            {
                dis[v[k]]=dis[u[k]]+w[k];//更新顶点1到顶点v[k]的路程
                //book数组用来判断顶点v[k]是否在队列中
                //如果不使用一个数组来标记的话,每次都要把队列中的元素进行遍历,很浪费时间
                if(!book[v[k]])
                {
                    book[v[k]]=1;
                    q.push(v[k]);
                }
            }
        }
        book[now]=0;//出队
    }
}

6.3 最小生成树

6.3.1 Kruskal

/*
Networking POJ - 1287 
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int f[11000];
int n,m;
void init()
{
    for(int i=1; i<=n; i++)
        f[i]=i;
}
//并查集寻找祖先的函数
int getf(int v)
{
    if(f[v]!=v)//路径压缩
        f[v]=getf(f[v]);
    return f[v];
}
//并查集合并两子集合的函数
int unite(int u,int v)
{
    int t1=getf(u);
    int t2=getf(v);
    if(t1!=t2)//判断两个点是否在同一个集合中
    {
        f[t2]=t1;
        return 1;
    }
    return 0;
}
struct node
{
    int u;
    int v;
    int w;
} q[11000];
//按照边的权值从小到大排序
bool cmp(node x,node y)
{
    return x.w<y.w;
}
int main()
{
    while(scanf("%d",&n)&&n)
    {
        scanf("%d",&m);
        init();//初始化
        int ans=0;
        for(int i=0; i<m; i++)
            scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].w);
        sort(q,q+m,cmp);
        int num=0;
        //Kruskal算法核心部分
        for(int i=0; i<m; i++)
        {
            //判断一条边的两个顶点是否已经连通,即判断是否已经在同一个集合中
            if(unite(q[i].u,q[i].v))//如果目前尚未连通,则选用这条边
            {
                ans+=q[i].w;
                num++;
            }
            if(num==n-1)//直到选用n-1条边之后退出循环
                break;
        }
        printf("%d\n",ans);
    }
    return 0;
}

6.3.2 Prim

/*
Networking POJ - 1287
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
int e[55][55];
int dis[55];
int book[55];
int n,m;
int ans;
void Prim()
{
    memset(book,0,sizeof(book));
    for(int i=1; i<=n; i++)
        dis[i]=inf;
    dis[1]=0;
    int mi;
    int u;
    for(int i=1; i<=n; i++)
    {
        mi=inf;
        for(int j=1; j<=n; j++)
        {
            if(!book[j]&&mi>dis[j])
            {
                mi=dis[j];
                u=j;
            }
        }
        ans+=dis[u];
        book[u]=1;
        for(int v=1; v<=n; v++)
        {
            if(!book[v]&&dis[v]>e[u][v])
                dis[v]=e[u][v];
        }
    }
}
int main()
{
    while(scanf("%d",&n)&&n)
    {
        scanf("%d",&m);
        ans=0;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if(i==j)
                    e[i][j]=0;
                else
                    e[i][j]=inf;
            }
        }
        int t1,t2,t3;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&t1,&t2,&t3);
            if(e[t1][t2]>t3)
                e[t2][t1]=e[t1][t2]=t3;
        }
        Prim();
        printf("%d\n",ans);
    }
    return 0;
}

6.4 拓扑排序

6.4.1 Sorting It All Out POJ - 1094

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char ans[30];
int n,m,num;
int book[30][30],s[30],t[30],mark[30];
int topsort()
{
    int l=0,flag=0;
    for(int i=1; i<=num; i++)
    {
        int k=-1;
        int cnt=0;
        for(int j=1; j<=n; j++)
        {
            if(mark[j]&&s[j]==0)
            {
                k=j;
                cnt++;
            }
        }
        if(k==-1)//*********
            return -1;
        if(cnt!=1)
            flag=1;
        s[k]=-1;
        ans[l++]='A'+(k-1);
        for(int i=1; i<=n; i++)
        {
            if(book[k][i])
                s[i]--;
        }
    }
    ans[l]=0;
    if(num==n&&!flag)
        return 1;
    return 0;
}
int main()
{
    while(~scanf("%d%d",&n,&m)&&(n||m))
    {
        num=0;//初始化
        int flag=0;
        char str[10];
        //清空函数
        memset(t,0,sizeof(t));
        memset(s,0,sizeof(s));
        memset(book,0,sizeof(book));
        memset(mark,0,sizeof(mark));
        //一边输入一边判断
        for(int i=1; i<=m; i++)
        {
            scanf("%s",str);
            if(flag!=0)
                continue;
            //将字符转化为数字    
            int t1=str[0]-'A'+1;
            int t2=str[2]-'A'+1;
            //将出现过的字符进行标记
            if(!mark[t1])
            {
                mark[t1]=1;
                num++;
            }
            if(!mark[t2])
            {
                mark[t2]=1;
                num++;
            }
            //进行拓扑排序的模拟
            if(!book[t1][t2])
            {
                t[t2]++;
                book[t1][t2]=1;
                //t数组将之前的数据
                for(int j=0; j<30; j++)
                    s[j]=t[j];
                int kk=topsort();
                if(kk==-1)
                {
                    printf("Inconsistency found after %d relations.\n",i);
                    flag=1;
                }
                else if(kk==1)
                {
                    printf("Sorted sequence determined after %d relations: %s.\n",i,ans);
                    flag=1;
                }
            }
        }
        if(!flag)
            printf("Sorted sequence cannot be determined.\n");
    }
    return 0;
}

6.5 二分图最大匹配

/*
输入:
3 5
1 1
1 2
2 2
2 3
3 1
输出:
3
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,k;
int e[110][110];
int match[110];
int book[110];
int dfs(int u)
{
    for(int i=1; i<=m; i++)
    {
        if(!book[i]&&e[u][i])
        {
            book[i]=1;//标记i点已经访问过
            //如果i未被配对,或者找到了新的配对
            if(!match[i]||dfs(match[i]))
            {
                match[i]=u;
                return 1;
            }
        }
    }
    return 0;
}
int solve()
{
    int ans=0;
    memset(match,0,sizeof(match));
    for(int i=1; i<=n; i++)
    {
        memset(book,0,sizeof(book));//清空上次搜索时的标记
        if(dfs(i))
            ans++;//寻找增广路,如果找到,匹配数加1
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);//n个点 m条边
    memset(e,0,sizeof(e));
    for(int i=0; i<m; i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        e[a][b]=1;
    }
    int ans=solve();    //ans为最大匹数
    printf("最大匹数: ");
    printf("%d\n",ans);
    return 0;
}

6.6 强连通分量 Tarjan

/*
Network of Schools POJ - 1236 
*/
#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
int n;
vector<int>v[110];
stack<int>s;
int dfn[110],low[110];
int book[110];//是否在栈中
int vis[110];//在同一个组内
int num;//访问的第几个数
int cnt;//多少组
int in[110];
int out[110];
void init()
{
    for(int i=0; i<=n; i++)
        v[i].clear();
    while(!s.empty())
        s.pop();
    num=0;
    cnt=0;
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(book,0,sizeof(book));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
}
void tarjan(int x)
{
    dfn[x]=low[x]=++num;
    s.push(x);
    book[x]=1;
    for(int i=0; i<v[x].size(); i++)
    {
        int y=v[x][i];
        if(!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(book[y])
        {
            low[x]=min(low[x],dfn[y]);
        }
    }
    if(dfn[x]==low[x])
    {
        int now;
        ++cnt;
        vis[x]=cnt;
        do
        {
            now=s.top();
            s.pop();
            vis[now]=cnt;
            book[now]=0;
        }while(now!=x);
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        init();
        for(int i=1; i<=n; i++)
        {
            int a;
            while(scanf("%d",&a)&&a)
            {
                v[i].push_back(a);
            }
        }
        for(int i=1; i<=n; i++)
        {
            if(!dfn[i])
                tarjan(i);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<v[i].size();j++)
            {
                int x=v[i][j];
                if(vis[i]!=vis[x])
                {
                    in[vis[x]]++;
                    out[vis[i]]++;
                }
            }
        }
        int sum1=0,sum2=0;
        for(int i=1;i<=cnt;i++)
        {
            if(in[i]==0)
                sum1++;
            if(out[i]==0)
                sum2++;
        }
        if(cnt==1)
            printf("1\n0\n");
        else
        printf("%d\n%d\n",sum1,max(sum1,sum2));
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值