北航2015集训队选拔赛解题报告

Preface

http://acm.buaa.edu.cn/contest/199/home/

A the battle is over

Description

NExPlain最近对ZooKeeper(动物园管理员)非常感兴趣。而有一天,他发现ZooKeeper碰到了这样一个问题:
我们知道动物园里有一排展区,总共n个,每个展区里面都有若干个动物。而所有动物现在都不慎中了毒,我们需要拯救这些动物。
ZooKeeper当然有解药。对于每次救援操作,ZooKeeper可以选择连续的m个展区的动物给他们解毒,但解药只有k个,意味着只能做k次操作。
现在问题来了:ZooKeeper最多能拯救多少只动物呢?

Input

输入文件包含多组数据,以EOF为结尾。
每组数据第一行三个整数 n,m,k 分别表示总展区个数,每次操作可以覆盖的连续的展区个数,可操作次数
第二行是n个整数ai, 表示第i个展区的动物数量
0 ≤ m ≤ n ≤ 1,000 , 0 ≤ k ≤ 1,000
0 ≤ ai ≤ 1,000,000

Output

每组数据输出一行答案,表示最多可以拯救的动物数量

Sample Input

5 2 2
1 2 3 4 5
3 2 2

Sample Output

14
6

Hint

同一个展区可以被多次操作覆盖,如样例2中第一次操作覆盖了{1,2}展区,第二次操作覆盖了{2,3}展区

题解

由于 nk 较小,所以直接dp即可,设 f[i][j] 表示对前 j 个展区用了i次所能获得的最大的值,因为只有两个决策,所以 f[i][j]=min{f[i][j1],f[i1][jm]+sum[jm+1][j]}

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
int f[1010][1010];
int n,m,k;
int a[1010];
int sum[1010];
int main()
{
    while (scanf("%d %d %d",&n,&m,&k)==3)
    {
        memset(f,0,sizeof(f));
        for (int i=1;i<=n;i++)
            scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
        for (int i=1;i<=k;i++)
        { 
            for (int j=1;j<=n;j++)
            {
                if (jelse
                {
                    f[i][j]=max(f[i-1][j-m]+sum[j]-sum[j-m],f[i][j-1]);
                }
            }
        }
        printf("%d\n",f[k][n]);
    }       
    return 0;
}

B another episode

Description

有多少含有n个正整数的序列满足其中所有数字的最大公约数是a,最小公倍数是b?
两个序列A,B不相同当且仅当存在一个(1≤i≤n)使得Ai≠Bi。
由于序列数量可能很大,只需要答案对1,000,000,007取模后的结果。

Input

输入文件包含多组数据,以EOF为结尾。
每组数据有三个正整数n , a , b。
1≤ n , a , b ≤1,000,000,000。

Output

对于每组数据输出序列个数对 1,000,000,007 取模。

Sample Input

2 1 6
5 3 9

Sample Output

4
30

Hint

样例1只有(1,6), (2,3), (3,2), (6,1)满足.

题解

显然当 b%a!=0 时,无解。否则我们考虑 d=b/a ,我们将 d 分解,得到 d=pa11pa22pann 。很容易发现,我们只需要分开考虑每个质因子的分配情况即可。对于 pa11 ,要分配给n个数,每个数都有 a1+1 种情况,但是这样不一定满足最大公约数和最小公倍数的限制。显然,这n个数中必有一个被分配到 p01 ,必有一个被分配到 pa11 。利用容斥原理计算不符合的情况即可。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
typedef long long LL;
const LL p=1e9+7;
LL multi(LL a, LL b)
{
    LL ret=0;
    while(b)
    {
        if(b&1) ret=(ret+a)%p;
        a=(a+a)%p;
        b>>=1;
    }
    return ret;
}
LL pow_mod(LL a,LL b)
{

    LL ret=1;
    while(b)
    {
        if(b&1) ret=multi(ret,a)%p;
        a=multi(a,a)%p;
        b>>=1;
    }
    return ret;
}
#define maxn 1000010
bool valid[maxn];
int primm;
LL prim[maxn];
void getPrime(int n)
{
    memset(valid,true,sizeof(valid));
    for (int i=2;i<=n;i++)
    {
        if (valid[i])
        {
            primm++;
            prim[primm]=i;
            for (int j=2;j<=n/i;j++)
              valid[i*j]=0;
        }
    }
}
LL n,a,b;
int num;
LL c[1000];
int main()
{
    getPrime(1000000);  
    while (cin>>n>>a>>b)
    {
        if (b%a!=0)
        {
            cout<<0<continue;
        }
        LL d=b/a;
        num=0;
        for (int i=1;i<=primm;i++)
        {
            if (d%prim[i]==0)
            {
                num++;c[num]=0;
                while (d%prim[i]==0) d/=prim[i],c[num]++;
            }
        }
        if (d>1) num++,c[num]=1;
        LL ans=1;
        for (int i=1;i<=num;i++)
        {
            LL res=pow_mod(c[i]+1,n)-pow_mod(c[i],n)-pow_mod(c[i],n)+pow_mod(c[i]-1,n);

            while (res<0) res+=p;
            ans=(ans*res)%p;
        }
        cout<return 0;
}

C once we were

Description

在一个n x m的国际象棋棋盘上有很多车(Rook),其中车可以攻击他所属的一行或一列,包括它自己所在的位置。

现在还有很多询问,每次询问给定一个棋盘内部的矩形,问矩形内部的所有格子是否都可以被车攻击到?

Input

输入文件包含多组数据,以EOF为结尾。
每组数据有4个正整数n , m , K , Q。
K为车的数量,Q为询问的个数。
接下来有K行,每行两个整数x , y , 表示车所在的坐标。
再接下来有Q行,每行4个整数x1 , y1 , x2 , y2,表示询问的矩形的左下角与右上角的坐标。

1≤ n , m , K , Q ≤100,000。
1≤ x ≤n , 1 ≤ y ≤ m。
1≤ x1 ≤ x2 ≤ n , 1 ≤ y1 ≤ y2 ≤ m。

Output

对于每组询问,输出Yes或No。

Sample Input

2 2 1 2
1 1
1 1 1 2
2 1 2 2
2 2 2 1
1 1
1 2
2 1 2 2

Sample Output

Yes
No
Yes

Hint

输入数据过大,建议使用scanf

题解

一个矩形都能被覆盖到,当且仅当矩形的每一列都有车或者矩形的每一行都有车。判断是否一列或一行都有车,利用前缀和计算区间和即可。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
int n,m,k,q;
int row[110000],col[110000];
int main()
{
    while (scanf("%d %d %d %d",&n,&m,&k,&q)==4)
    {
        memset(row,0,sizeof(row));
        memset(col,0,sizeof(col));
        for (int i=1;i<=k;i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            row[x]=1;
            col[y]=1;
        }
        for (int i=1;i<=n;i++)
            row[i]+=row[i-1];
        for (int i=1;i<=m;i++)
            col[i]+=col[i-1];
        int x1,x2,y1,y2;
        for (int i=1;i<=q;i++)
        {
            scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
            if (x2if (y2if (row[x2]-row[x1-1]==x2-x1+1||col[y2]-col[y1-1]==y2-y1+1) puts("Yes");
            else puts("No");
        }
    }       
    return 0;
}

D wanna destroy?

Description

某个实验室的平面图可以看作是划分为n x m个方格的矩形,其中实验室内部已经有了很多设备,每个设备在平面图上也可以看做是一个平行于坐标轴的矩形,正好占据着一些方格。

现在实验室购进了一种新的设备,现在问题是该设备有多少种摆放方式使得新设备不能占据原来的设备的方格?

该种新设备可以看作一个1 x K的矩形,新设备的摆放要求与原有设备一致,两种摆放方式不同当且仅当新设备占据的方格集合不同。

Input

输入文件包含多组数据,以EOF为结尾。
第一行有4个整数n , m , p , K , 表示房间的长和宽,原有设备的数量以及新设备的长。
接下来有p行,每行4个整数x1 , y1 , x2 , y2,表示原有设备占据方格的左下角与右上角的坐标。
保证所有方格不会被超过1个设备占据。

1 ≤ n, m, p, K ≤ 100,000
1≤ x1 ≤ x2 ≤ n , 1 ≤ y1 ≤ y2 ≤ m。

Output

对于每组数据输出有多少种方式。

Sample Input

3 3 1 2
2 2 2 2
3 3 1 3
2 2 2 2
2 3 2 2
1 1 1 1
2 3 2 3

Sample Output

8
4
3

题解

如果 K!=1 我们需要分开讨论,新设备横着放和竖着放这两种情况。很容易想到扫描线,由于设备之间不会覆盖,所以不用线段树来维护。只需要一个multiset加lower_bound()来找空着的位置然后更新答案即可。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
typedef long long LL;
int n,m,p,k;
struct event
{
    int a,b,c,key;
}a[410000];
struct rec
{
    int x1,x2,y1,y2;
}rects[110000];
multiset<int> S;
multiset<int>::iterator it;
int num;
int cmp(event a,event b)
{
    if (a.c!=b.c) return a.creturn a.keyint l,int r)
{
    if (l>r) return 0;
    LL ll=(LL)r-l+1;
    return max(ll-k+1,0LL);
}
int main()
{
    while (scanf("%d %d %d %d",&n,&m,&p,&k)==4)
    {
        for (int i=1;i<=p;i++)
        {
            scanf("%d %d %d %d",&rects[i].x1,&rects[i].y1,&rects[i].x2,&rects[i].y2);
        }
        S.clear();
        ans=0;
        num=0;
        for (int i=1;i<=p;i++)
        {
            num++;
            a[num].a=rects[i].y1,a[num].b=rects[i].y2,a[num].c=rects[i].x1,a[num].key=1;
            num++;
            a[num].a=rects[i].y1,a[num].b=rects[i].y2,a[num].c=rects[i].x2,a[num].key=3;
        }
        for (int i=1;i<=n;i++)
        {
            num++;
            a[num].c=i,a[num].key=2;
        }
        sort(a+1,a+1+num,cmp);
        S.insert(0);
        S.insert(m+1);
        LL now=(LL)max(m-k+1,0);
        for (int i=1;i<=num;i++)
        {
            if (a[i].key==1)
            {
                it=S.lower_bound(a[i].a);
                int l,r=*(it);
                l=*(--it);
                now=now-cal(l+1,r-1)+cal(l+1,a[i].a-1)+cal(a[i].b+1,r-1);
                S.insert(a[i].a),S.insert(a[i].b);
            }
            if (a[i].key==2)
            {
                ans+=now;
            }
            if (a[i].key==3)
            {
                it=S.lower_bound(a[i].a);
                int ll=*it;
                int rr=*(++it);
                int r=*(++it);
                it--,it--,it--;
                int l=*(it);
                //int l=*(it-1),r=*(it+2),ll=*(it),rr=*(it+1);
                //cout<
                now=now-cal(l+1,ll-1)-cal(rr+1,r-1)+cal(l+1,r-1);
                S.erase(ll),S.erase(rr);

            }
        }
        if (k==1)
        {
            cout<continue;
        }
        num=0;
        for (int i=1;i<=p;i++)
        {
            num++;
            a[num].a=rects[i].x1,a[num].b=rects[i].x2,a[num].c=rects[i].y1,a[num].key=1;
            num++;
            a[num].a=rects[i].x1,a[num].b=rects[i].x2,a[num].c=rects[i].y2,a[num].key=3;
        }
        for (int i=1;i<=m;i++)
        {
            num++;
            a[num].c=i,a[num].key=2;
        }
        sort(a+1,a+1+num,cmp);
        S.clear();
        S.insert(0);
        S.insert(n+1);
        now=(LL)max(n-k+1,0);
        for (int i=1;i<=num;i++)
        {
            if (a[i].key==1)
            {
                it=S.lower_bound(a[i].a);
                int l,r=*(it);
                l=*(--it);
                now=now-cal(l+1,r-1)+cal(l+1,a[i].a-1)+cal(a[i].b+1,r-1);
                S.insert(a[i].a),S.insert(a[i].b);
            }
            if (a[i].key==2)
            {
                ans+=now;
            }
            if (a[i].key==3)
            {
                it=S.lower_bound(a[i].a);
                int ll=*it;
                int rr=*(++it);
                int r=*(++it);
                it--,it--,it--;
                int l=*(it);
                //int l=*(it-1),r=*(it+2),ll=*(it),rr=*(it+1);
                //cout<
                now=now-cal(l+1,ll-1)-cal(rr+1,r-1)+cal(l+1,r-1);
                S.erase(ll),S.erase(rr);

            }
        }
        cout<return 0;
}

E this is my despair

Description

给定一个n x m的矩阵,求该矩阵的模Q意义下的最大和子矩阵。

模Q意义下的和是指任意加法运算结束后结果都要对Q取模。

Input

输入文件包含多组数据,以EOF为结尾。
每组数据有3个正整数n , m , Q。
接下来有n行,每行m个数,表示这个矩阵。

1 ≤ n , m ≤ 200
1 ≤ Q ≤ 1,000,000,000
-1,000,000,000 ≤ 矩阵元素 ≤ 1,000,000,000

Output

对于每组数据,输出和最大子矩阵的值。

Sample Input

2 2 4
1 1
1 1
2 3 2
2 8 4
6 2 8
2 2 4
-1 -1
-1 -1

Sample Output

2
0
3

Hint

这个问题我们假定 -1 mod 4 = 3

题解

首先枚举矩形的起始行和终止行,之后计算每一列的数字之和。考虑新数组的权值和,以 i 为列结束位置所能产生的最大的模Q的子矩形一定只有两种情况,一种是从第1列到第 i 列,另一种是找最小的大于 i 处前缀和的值。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
typedef long long LL;
int n,m;
LL Q;
LL a[210][210];
set S;
set::iterator it;
int main()
{
    while (scanf("%d %d %lld",&n,&m,&Q)==3)
    {
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=m;j++)
            {
                scanf("%lld",&a[i][j]);
                a[i][j]=((abs(a[i][j])/Q+1LL)*Q+a[i][j])%Q;
                a[i][j]=(a[i][j]+a[i-1][j])%Q;
            }
        }
        LL ans=0;
        for (int i=1;i<=n;i++)
            for (int j=i;j<=n;j++)
            {
                LL x=0;
                S.clear();
                for (int k=1;k<=m;k++)
                {
                    x=(x+a[j][k]-a[i-1][k]+Q)%Q;
                    while (x<0) x+=Q;
                    ans=max(ans,x);
                    it=S.upper_bound(x);
                    LL y=(x-(*it))%Q;
                    while (y<0) y+=Q;
                    if (it!=S.end()) ans=max(ans,y);
                    S.insert(x);
                }
            }
        cout<return 0;
}

F raise the curtain

Description

给定一个无向图,边带权。

接下来有很多操作,一个操作要么删去图中的一个点,要么询问某两点间的最短路。

Input

输入文件包含多组数据,以EOF为结尾。
每组数据有三个正整数n , m , Q,表示这个无向图的点数与边数与接下来的操作数。
接下来有m行,每行三个数x , y , w,表示x和y之间有一条长度为w的无向边。

再接下来有Q行,每行是一下两种情况:
0 x - 删去x这个节点
1 x y - 询问从x到y的最短路,保证询问的点在原图中存在。

1 ≤ n ≤ 300
0 ≤ m ≤ n2
0 ≤ Q , w ≤ 100,000
1 ≤ x , y ≤ n

Output

对于每个询问输出最短路的长度,如果不存在从x到y的路径输出-1。

Sample Input

3 3 3
1 3 5
2 3 10
1 2 20
1 1 2
0 3
1 1 2
3 2 3
1 2 1
2 3 2
1 1 3
0 2
1 1 3

Sample Output

15
20
3
-1

题解

很容易想到离线倒序修改。把删点改为了加点。利用floyd,每加入一个点 x ,进行循环,将 x 设为中间点,更新 f[i][j] 的值即可。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
const int INF=1e8;
int n,m,Q;
int g[310][310],f[310][310];
int op[110000],x[110000],y[110000];
int pp[310];
int a,b,c;
int num;
int way[110000];
int main()
{
    while (scanf("%d %d %d",&n,&m,&Q)==3)
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                if (i!=j) g[i][j]=INF;
        for (int i=1;i<=m;i++)
        {
            scanf("%d %d %d",&a,&b,&c);
            g[b][a]=g[a][b]=min(g[a][b],c);
        }
        for (int i=1;i<=n;i++)  
            for (int j=1;j<=n;j++)
                f[i][j]=g[i][j];
        memset(pp,0,sizeof(pp));
        for (int i=1;i<=Q;i++)
        {
            scanf("%d",&op[i]);
            if (op[i]==0) scanf("%d",&x[i]),pp[x[i]]=1;
            else scanf("%d %d",&x[i],&y[i]);
        }
        for (int k=1;k<=n;k++)
            if (!pp[k])
                for (int i=1;i<=n;i++)
                    for (int j=1;j<=n;j++)
                        f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
        num=0;
        for (int k=Q;k>=1;k--)
        {
            if (op[k]==0)
            {
                for (int i=1;i<=n;i++)
                    for (int j=1;j<=n;j++)
                        f[i][j]=min(f[i][j],f[i][x[k]]+f[x[k]][j]);
            }
            else
            {
                num++;
                if (f[x[k]][y[k]]!=INF) way[num]=f[x[k]][y[k]];
                else way[num]=-1;
            }
        }
        for (int i=num;i>=1;i--)
            printf("%d\n",way[i]);

    } 
    return 0;
}

F absolute configuration

Description

给你n个数, 再计算任意两个数差的绝对值,这样我们可以得到n*(n-1)/2个数。
求这n*(n-1)/2个数的中位数是多少?
如果中位数不唯一,则输出其中最小的。

Input

输入文件包含多组数据,以EOF为结尾。
数据第一行是n,代表有n个数字。
接下来一行有n个数,代表这写数字分别是什么。

2 ≤ n ≤ 500,000
-1,000,000,000 ≤ 数字 ≤ 1,000,000,000

Output

对于每组数据输出得到的中位数是多少。

Sample Input

4
1 3 2 4
3
1 10 2

Sample Output

1
8

Hint

输入数据过大,建议使用scanf

题解

二分中位数的值 x ,利用尺取法O(n)计算出所有绝对值中小于 x 的个数,然后更新ans即可。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
typedef long long LL;
const int INF=1e9;
int n;
int a[600000];
LL check(LL x)
{
    LL res=0;
    LL l=0,r=0;
    for (LL i=0;iwhile (a[i]-a[l]>=x&&lwhile (a[r+1]-a[i]1) r++;
        res+=r-i;
    }
    return res/2LL;
}
int main()
{
    while (scanf("%d",&n)==1)
    {
        for (int i=0;iscanf("%d",&a[i]);
        sort(a,a+n);
        LL l=0,r=2e9;
        LL pos=(LL)n*(n-1)/2LL;
        pos=(pos+1LL)/2LL;
        int ans=0;
        while(l<=r)
        {
            LL mid=(l+r)>>1;
            LL key=check(mid);
            if (key1,ans=mid;
            else r=mid-1;
        }
        printf("%d\n",ans);
    } 
    return 0;
}

H I cursed myself

Description

有一条数轴,还有一个区间的集合,集合大小为n。
现在等概率的从集合中选出集合的一个子集,求取出的子集的区间并的期望长度。
空集的区间并长度被认为是0。

Input

输入文件包含多组数据,以EOF为结尾。
第一行为集合的大小n。
接下来的n行,每行两个数l , r代表集合内区间的左右端点坐标。

1≤ n ≤100,000。
-1,000,000,000 ≤ l ≤ r ≤ 1,000,000,000

Output

对于每组数据,输出 期望乘2^n 对 1,000,000,007 取模的结果。

Sample Input

1
0 1
2
0 2
1 3

Sample Output

1
7

Hint

对于第二个例子,期望为1.75
1.75 x 2^2 = 7

题解

题目要求的实际上是所有子集对应的区间并的长度之和。首先将所有区间离散化,考虑每一个小区间,假设这个小区间被 m 个区间覆盖,那么这个小区间不被包含于一个子集的区间并,当且仅当这m个区间都不在这个子集中。这样就可以计算了。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
typedef long long LL;
const LL p=1e9+7;

int a[210000];
LL len[210000];
LL l[210000],r[210000];
int n,num;

vector  q;
LL v[210000];
LL f[210000];
int main()
{
    f[0]=1;
    for (int i=1;i<=110000;i++)
        f[i]=(2*f[i-1])%p;
    while(scanf("%d",&n)==1)
    {
        q.clear();
        for (int i=1;i<=n;i++)
        {
            scanf("%lld %lld",&l[i],&r[i]);
            q.push_back(l[i]),q.push_back(r[i]);
        }
        num=0;
        sort(q.begin(),q.end());
        vector::iterator it=unique(q.begin(),q.end());
        q.erase(it,q.end());
        for(int i=0;i0;
            a[num]=q[i];
            if (num!=1)
            {
                len[num]=q[i]-q[i-1];
            }
        }
        for (int i=1;i<=n;i++)
        {
            l[i]=lower_bound(a+1,a+1+num,l[i])-a;
            r[i]=lower_bound(a+1,a+1+num,r[i])-a;
        }
        for (int i=1;i<=n;i++)
            if (l[i]!=r[i]) v[l[i]+1]++,v[r[i]+1]--;
        LL ans=0,xx=f[n];
        int y=0;
        for (int i=2;i<=num;i++)
        {
            y+=v[i];
            //cout<
            LL yy=f[n-y];
            LL ll=(xx-yy+p)%p;
            ans=(ans+ll*len[i])%p;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

I happy ending

Description

给定一个长度为n的字符串,求它所有子串中字典序排第k位(即第k小)的串。

Input

输入文件包含多组数据,以EOF为结尾。
第一行为n , k
第二行为该字符串

1≤ n , k ≤100,000。
字符串只包含小写字母

Output

对于每组数据,如果该子串不存在,输出”No such substring.”,否则输出这个子串。

Sample Input

2 2
aa
3 5
abc
4 7
abab
1 233
a

Sample Output

a
bc
b
No such substring.

题解

利用后缀数组的height数组进行模拟。设 now 为当前枚举的后缀, w[i] 表示每个后缀枚举到了哪一位, head 表示最早的没被枚举完的后缀是哪一个。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
typedef long long LL;
int n,k;
int height[110000],rank[110000],sa[110000],h[110000],a[110000],b[110000];
char st[110000];
template<typename T>
void radix(int a[],int b[],T s[],int m,int n)
{   int i;
    for (i=0;i<=m;++i)
        h[i]=0;
    for (i=1;i<=n;++i)
        ++h[s[a[i]]];
    for (i=1;i<=m;++i)
        h[i]+=h[i-1];
    for (i=n;i>0;--i)
        b[h[s[a[i]]]--]=a[i];
}
void init_sa()
{   int i,j;
    for (i=1;i<=n;++i)
        rank[i]=i;
    radix(rank,sa,st,255,n);
    rank[sa[1]]=1;
    for (i=2;i<=n;++i)
        if (st[sa[i]]!=st[sa[i-1]])
            rank[sa[i]]=rank[sa[i-1]]+1;
        else rank[sa[i]]=rank[sa[i-1]];
    for (i=1;i<=n;i*=2)
    {   for (j=1;j<=n;++j)
        {   a[j]=rank[j];
            if (i+j<=n)
                b[j]=rank[i+j];
            else b[j]=0;
            sa[j]=j;
        }
        radix(sa,rank,b,n,n);
        radix(rank,sa,a,n,n);
        rank[sa[1]]=1;
        for (j=2;j<=n;++j)
            if (a[sa[j]]!=a[sa[j-1]]||b[sa[j]]!=b[sa[j-1]])
                rank[sa[j]]=rank[sa[j-1]]+1;
            else rank[sa[j]]=rank[sa[j-1]];
        if (rank[sa[n]]==n)
            break ;
    }
}
void calc_height()
{   int i,p=0;
    for (i=1;i<=n;++i)
    {   if (p)
            --p;
        if (rank[i]!=1)
            while (st[i+p]==st[sa[rank[i]-1]+p]&&st[i+p]!='|')
                ++p;
        height[rank[i]]=p;
    }
}

int now,head,w[110000];
int main()
{
    while(scanf("%d %d",&n,&k)==2)
    {
        scanf("%s",st);
        for (int i=n;i>=1;i--)
            st[i]=st[i-1];
        if (k>1LL*n*(n+1)/2)
        {
            printf("No such substring.");
            continue;
        }
        init_sa();
        calc_height();
        memset(w,0,sizeof(w));
        now=1,w[1]=1,head=1;
        for (int i=2;i<=k;i++)
        {
            while (w[head]==n-sa[head]+1) head++;
            if (now==n)
            {
                now=head;
                w[now]++;
                continue;
            }
            if (height[now+1]>=w[now])
            {
                now++;
                w[now]++;
            }
            else
            {
                now=head;
                w[now]++;
            }
        }
        for (int i=sa[now];i<=sa[now]+w[now]-1;i++)
            printf("%c",st[i]);
        printf("\n");
    }
    return 0;
}

你可能感兴趣的:(解题报告)