比赛链接:http://codeforces.com/contest/822

A. I’m bored with life

解法:水题,模拟一下。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int A,B;
    scanf("%d%d",&A,&B);
    int C=min(A,B),ans=1;
    for(int i=1; i<=C; i++)ans=ans*i;
    printf("%d\n",ans);
}

B. Crossword solving

题意:给出两个字符串,可以把第一个字符串任意位置变成任意的字母使他成为另一个的子串,求最少变的数量和位置。
解法:O(n^2)暴力匹配

#include <bits/stdc++.h>
using namespace std;

int n,m;
char s1[2000],s2[2000];
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s",s1+1);
    scanf("%s",s2+1);
    int mx = 100000, st=0, en=0;
    for(int i=1; i<=m&&i+n-1<=m; i++){
        int temp = 0;
        for(int j=1; j<=n; j++){
            if(s2[i+j-1]!=s1[j]){
                temp++;
            }
        }
        if(temp<mx){
            mx=temp;
            st=i;
            en=i+n-1;
        }
    }
    printf("%d\n", mx);
    int j=0;
    for(int i=st; i<=en; i++){
        ++j;
        if(s1[j]!=s2[i]){
            printf("%d ",j);
        }
    }
    printf("\n");
}

C. Hacker, pack your bags!

题意:给你n个旅券,上面有开始时间l,结束时间r,和花费cost,要求选择两张时间不相交的旅券时间长度相加为x,且要求花费最少。

解法:首先我们把给定的数据分成两份,第一份按照出去的时间升序排序,第二份按照回来的时间升序排序,之后遍历第一份,这样可以根据第一份的出去的时间来决定第二份回来的时间,然后维护一个最小花费数组mincost,对于每一个数据求出t=x-(r-l+1),然后更新最小值就可以了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 200010;
struct node{
    int l,r,cost;
}a[maxn],b[maxn];
int n, x, mi[maxn];
bool cmp1(node x, node y){
    return x.l<y.l;
}
bool cmp2(node x, node y){
    return x.r<y.r;
}
int main()
{
    scanf("%d%d",&n,&x);
    for(int i=1; i<=n; i++){
        scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].cost);
        b[i].l=a[i].l,b[i].r=a[i].r,b[i].cost=a[i].cost;
    }
    for(int i=0; i<maxn; i++) mi[i]=INT_MAX;
    int pos = 1;
    sort(a+1,a+n+1,cmp1);
    sort(b+1,b+n+1,cmp2);
    int ans = INT_MAX;
    for(int i=1; i<=n; i++){
        while(b[pos].r<a[i].l&&pos<=n){
           mi[b[pos].r-b[pos].l+1] = min(mi[b[pos].r-b[pos].l+1], b[pos].cost);
            ++pos;
        }
        int y = x - (a[i].r-a[i].l+1);
        if(y>0&&mi[y]!=INT_MAX){
            ans = min(ans, a[i].cost+mi[y]);
        }
    }
    if(ans==INT_MAX) ans=-1;
    printf("%d\n", ans);
    return 0;
}

D. My pretty girl Noora

题意:N个人的比赛,将N个人平均分成N/x组,每组恰好x人。对于每组对F[N]的贡献为x*(x-1)/2,同时有N/x人晋级,其后续操作对f[N]的贡献为f[N/x]。直到仅剩下一个吻为止。其中f[N]可以通过改变每次x的值使得结果不同。给定t , l , r 求区间[l, r]的每个f[i],按公式t^0*f(l) + t^1*f(l+1) + ”’ + t^(r-l)*f(r)求最小的解。

解题方法:
求公式的最小解即要求使得每个 f[i] 尽可能小 。
当 i 为质数时,其仅能均分为 1 组,f[i]=x*(x-1)/2 。
当 i 为合数时,最小的 f[i] = f[minFactor]*(i/minFactor)+f[i/minFactor] ,其中 minFactor 指 i 的最小质数因子。
故考虑通过类似素数筛法的形式,提前筛出每个数的最小素数因子,线性套用公式求解 f[i] 。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
const int maxn = 5e6+7;
LL dp[maxn];
LL t, l, r;
int main()
{
    for(LL i=0; i<maxn; i++) dp[i] = i*(i-1)/2;
    for(LL i=2; i<maxn; i++){
        for(LL j=2*i; j<min(1LL*maxn, i*i+1); j+=i){
            dp[j] = min(dp[j], dp[j/i]+i*(i-1)/2*(j/i));
            dp[j] = min(dp[j], dp[i]+j/i*(j/i-1)/2*i);
        }
    }
    scanf("%lld%lld%lld",&t,&l,&r);
    LL ans = 0, ret = 1;
    for(LL i=l; i<=r; i++){
        ans = (ans+ret*(dp[i]%mod)%mod)%mod;
        ret = ret*t%mod;
    }
    printf("%lld\n", ans);
    return 0;
}

E. Liar

题意:给你两个字符串,S,T。你可以将S分成任意块编号,至多选出X块编号递增的顺序组成新的串。问你是否能组成T串

解法:设定dp[i][j],表示 0~(i-1)之前 挑选了j块 到达的最大位置
那么对于S串的 i 位置,和T串的dp[i][j] + 1这个位置开始,最长公共前缀是多少?
假设为t , 那么转移就是 dp[i + t][j+1] = max(dp[i + t][j+1] , dp[i][j] + t);
   我们用后缀数组预处理出来lcp,那么查询的时候直接利用RMQ的O(1) 查询就好了
   杂度O(nlogn + n*30)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5+10;
int sa[maxn], t1[maxn], t2[maxn], c[maxn], Rank[maxn], height[maxn];
void build_sa(int s[], int n, int m){
    int i,j,p,*x=t1,*y=t2;
    for(i=0; i<m; i++) c[i]=0;
    for(i=0; i<n; i++) c[x[i]=s[i]]++;
    for(i=1; i<m; i++) c[i]+=c[i-1];
    for(i=n-1; i>=0; i--) sa[--c[x[i]]]=i;
    for(j=1; j<=n; j<<=1){
        p=0;
        for(i=n-j;i<n;i++) y[p++]=i;
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0; i<m; i++) c[i]=0;
        for(i=0; i<n; i++) c[x[y[i]]]++;
        for(i=1; i<m; i++) c[i]+=c[i-1];
        for(i=n-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(i=1; i<n; i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        if(p>=n) break;
        m=p;
    }
}
void getHeight(int s[], int n){
    int i,j,k=0;
    for(i=0; i<=n; i++) Rank[sa[i]]=i;
    for(i=0; i<n; i++){
        if(k) k--;
        j=sa[Rank[i]-1];
        while(s[i+k]==s[j+k]) k++;
        height[Rank[i]]=k;
    }
}
int f[maxn][33]; //f[i][j]表示表示0~(i-1)之前 挑选了j块 到达的最大位置
int n,m,dp[maxn][30];
int r[maxn];
char s[maxn], t[maxn];
void Lcp_init(){
    for(int i=1; i<=n+m+1; i++) dp[i][0] = height[i];
    for(int j=1; (1<<j)-1<=n+m+1; j++){
        for(int i=1; i+(1<<j)<=n+m; i++){
            dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
        }
    }
}
int lcp(int l, int r){
    l = Rank[l], r = Rank[r];
    if(l>r) swap(l, r);
    ++l;
    int k=0,len=r-l+1;
    while((1<<(k+1))<=len) ++k;
    return min(dp[l][k], dp[r-(1<<k)+1][k]);
}

int main()
{
   scanf("%d%s%d%s", &n,s,&m,t);
   for(int i=0; i<n; i++) r[i]=s[i]-'a'+1;
   r[n] = '*';
   for(int i=n+1; i<n+m+1; i++) r[i]=t[i-n-1]-'a'+1;
   r[n+m+1]=0;
   build_sa(r,n+m+2,256);
   getHeight(r, n+m+1);
   Lcp_init();
   int x, ans=0;
   scanf("%d", &x);
   for(int i=0; i<=n; i++){
        for(int j=0; j<=x; j++){
            f[i+1][j] = max(f[i+1][j], f[i][j]);
            int t = lcp(i, f[i][j]+n+1);
            f[i+t][j+1] = max(f[i+t][j+1], f[i][j]+t);
            ans = max(ans, f[i][j]);
        }
   }
   if(ans == m) puts("YES");
   else puts("NO");
   return 0;
}

F,题面太长了。目前已经弃疗。