Codeforces Round #244 (Div. 2)(强连通分量,后缀数组)

A. Police Recruits

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
int main()
{
    //freopen("in.txt","r",stdin);
    cin>>n;
    int ans=0,x,p=0;
    for(int i=0;i<n;i++)
    {
        cin>>x;
        if(x<0)
        {
            if(p>0)p--;
            else ans++;
        }
        else p+=x;
    }
    cout<<ans<<endl;
    return 0;
}

B. Prison Transfer
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200010;
int a[maxn];
int n,t,c;
int main()
{
    cin>>n>>t>>c;
    for(int i=1;i<=n;i++)cin>>a[i];
    int ans=0,cnt=0;
    for(int i=1;i<=c;i++)
        if(a[i]>t)cnt++;
    if(n==c&&cnt<=0)ans++;
    for(int i=c+1;i<=n;i++)
    {
        if(cnt<=0)ans++;
        if(a[i]>t)cnt++;
        if(a[i-c]>t)cnt--;
        if(i==n&&cnt<=0)ans++;
    }
    cout<<ans<<endl;
    return 0;
}

C. Checkposts
题意 : 给你n个点,m条有向边,每个点都有权值,你需要挑选若干个点建立post,要求使每个点都可以到达至少一个post,也至少可以由一个post到达。求挑选的点的最小权值和以及方案数。

思路:强连通,找出每个分量中权值最小的相加,方法数则相乘即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int INF=1000000000;
const int MOD=1000000007;
int n,m;
int lowlink[maxn],pre[maxn],sccno[maxn],dfs_clock,scc_cnt,a[maxn];
vector<int> grid[maxn],belong[maxn];
stack<int> s;
void dfs(int u)
{
    pre[u]=lowlink[u]=++dfs_clock;
    s.push(u);
    for(int i=0;i<grid[u].size();i++)
    {
        int v=grid[u][i];
        if(!pre[v])
        {
            dfs(v);
            lowlink[u]=min(lowlink[u],lowlink[v]);
        }
        else if(!sccno[v])lowlink[u]=min(lowlink[u],pre[v]);
    }
    if(lowlink[u]==pre[u])
    {
        ++scc_cnt;
        int x;
        while(true)
        {
            x=s.top();s.pop();
            sccno[x]=scc_cnt;
            belong[scc_cnt].push_back(x);
            if(x==u)break;
        }
    }
}
void find_scc()
{
    dfs_clock=scc_cnt=0;
    memset(pre,0,sizeof(pre));
    memset(sccno,0,sizeof(sccno));
    for(int i=1;i<=n;i++)
        if(!pre[i])dfs(i);
}
int main()
{
    //freopen("in.txt.txt","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    cin>>m;
    while(m--)
    {
        int u,v;
        cin>>u>>v;
        grid[u].push_back(v);
    }
    find_scc();
    long long ans=0,p=1;
    //cout<<scc_cnt<<endl;
    for(int i=1;i<=scc_cnt;i++)
    {
        int min1=INF,num=0;
        for(int j=0;j<belong[i].size();j++)
        {
            int v=belong[i][j];
            if(a[v]<min1){min1=a[v];num=0;}
            if(a[v]==min1)num++;
        }
        //cout<<<<endl;
        ans+=min1,p=p*num%MOD;
    }
    cout<<ans<<" "<<p<<endl;
    return 0;
}

D. Match & Catch

Police headquarter is monitoring signal on different frequency levels. They have got two suspiciously encoded strings s1 and s2 from two different frequencies as signals. They are suspecting that these two strings are from two different criminals and they are planning to do some evil task.

Now they are trying to find a common substring of minimum length between these two strings. The substring must occur only once in the first string, and also it must occur only once in the second string.

Given two strings s1 and s2 consist of lowercase Latin letters, find the smallest (by length) common substring p of both s1 and s2, where p is a unique substring in s1 and also in s2. See notes for formal definition of substring and uniqueness.

Input

The first line of input contains s1 and the second line contains s2 (1 ≤ |s1|, |s2| ≤ 5000). Both strings consist of lowercase Latin letters.

Output

Print the length of the smallest common unique substring of s1 and s2. If there are no common unique substrings of s1 and s2 print -1.

Sample test(s)
Input
apple
pepperoni
Output
2
Input
lover
driver
Output
1
Input
bidhan
roy
Output
-1
Input
testsetses
teeptes
Output
3
Note

Imagine we have string a = a1a2a3...a|a|, where |a| is the length of string a, and ai is the ith letter of the string.

We will call string alal + 1al + 2...ar (1 ≤ l ≤ r ≤ |a|) the substring [l, r] of the string a.

The substring [l, r] is unique in a if and only if there is no pair l1, r1 such that l1 ≠ l and the substring [l1, r1] is equal to the substring [l, r] in a.


后缀数组:对于位于字符串str1和str2的两个位置p(str1中)和q(str2中):记suffix(p)和str1的LCP长度为len1,suffix(q)和str2的LCP长度为len2,suffix(p)和suffix(q)的LCP长度为len。其中suffix(i)表示从位置i开头的后缀,LCP是最长公共前缀,那么题目要求的就是满足条件len > max(len1, len2)的最小的max(len1, len2)。取答案为ans = max(len1, len2) + 1,那么子串(p..p+ans-1)在str1中惟一,因为不惟一的最长子串长度为ans - 1。同样q也惟一。同时(p..p+ans-1)是str1和str2的公共子串(它是suffix(p)和suffix(q)的公共子串)。所以原问题就变成了一个纯后缀数组

不过有个地方不是很明白,为什么判断的时候是(h[i]>h[i-1]&&h[i]>h[i+1])?


#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=1000000000;
const int maxn=10010;
string s1,s2;
int n,lens1;
int sa[maxn],t[maxn],t2[maxn],c[maxn];
void build_sa(int m)
{
    int *x=t,*y=t2;
    for(int i=0;i<m;i++)c[i]=0;
    for(int i=0;i<n;i++)c[x[i]=s1[i]]++;
    for(int i=1;i<m;i++)c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
        for(int i=n-k;i<n;i++)y[p++]=i;
        for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
        for(int i=0;i<m;i++)c[i]=0;
        for(int i=0;i<n;i++)c[x[y[i]]]++;
        for(int i=1;i<m;i++)c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(int i=1;i<n;i++)
            x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++;
        if(p>=n)break;
        m=p;
    }
}
int rank[maxn],h[maxn];
void getHeight()
{
    int k=0;
    for(int i=0;i<n;i++)rank[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rank[i]-1];
        while(s1[i+k]==s1[j+k])k++;
        h[rank[i]]=k;
    }
}
void solve()
{
    lens1=s1.size();
    s1+='$';
    s1+=s2;
    n=s1.size();
    build_sa(123);
    getHeight();
    int ans=INF;
    for(int i=2;i<n;i++)
    {
        if((sa[i]<lens1)!=(sa[i-1]<lens1))
        {
            if(h[i]>h[i-1]&&h[i]>h[i+1])
                ans=min(ans,max(h[i+1],h[i-1])+1);
        }
    }
    printf("%d\n",ans==INF?-1:ans);
}
int main()
{
    //freopen("in.txt","r",stdin);
    cin>>s1>>s2;
    solve();
    return 0;
}

E. Police Patrol
题意: 有n个犯罪地点,我们需要选择一个地方作为警察局,从这里出发抓住所有的罪犯,警车做多可以装m个罪犯。求要抓住所有的罪犯的路程最小值是多少。

为什么奇数的时候要选n/2+1

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000010;
int n,m;
int a[maxn];
int main()
{
   
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    long long ans=0;
    int cnt=0,x=1,mid;
    if(n%2)mid=n/2+1;
    else mid=n/2;

    for(int i=1;i<mid;i++)
    {
        cnt++;
        if(cnt>=m){ans+=((long long)a[mid]-a[x])*2;cnt=0;x=i+1;}
    }
    if(cnt>0)ans+=((long long)a[mid]-a[x<1?1:x])*2;
    cnt=0;
    x=n;
    for(int i=n;i>mid;i--)
    {
        cnt++;
        if(cnt>=m){ans+=((long long)a[x]-a[mid])*2;cnt=0;x=i-1;}
    }
    if(cnt>0)ans+=((long long)a[x>n?n:x]-a[mid])*2;
    printf("%I64d\n",ans);
    //cout<<ans<<endl;
    return 0;
}

大神的代码

#include<cstdio>
int a[1000010];
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        int p1=0,p2=n-1;
        __int64 ans=0;
        while(p2-p1>0)//不断逼近
        {
            ans=ans+a[p2]-a[p1];
            p2=p2-m;//要想总距离最少,就必须先捉最左m个和最右m个
            p1=p1+m;
        }
        ans=ans*2;
        printf("%I64d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值