2016 Multi-University Training Contest 4

2016 Multi-University Training Contest 1

这次比赛名次算是目前来最好的一次了,总共A了6题,几乎全是dp题。真是佩服一神。

HDU5763 Another Meaning  (kmp+dp)

这道题开场后五分钟一神就给我们过来讲解法,我思考了好一会儿才理解。

kmp预处理出子串p。对于父串,当枚举当i时,如果t[i]是p的后缀,那么就dp[i] = (dp[i-m]+dp[i])%mod,(m是子串的长度)。即要么和t[i]相连的串变成*(此时个数是dp[i-m]个),要么和t[i]相连的串不变,那就继承前面的dp[i-1].(前面已有dp[i] = dp[i-1]).

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const ll mod = 1000000007;
char p[maxn];
char t[maxn];
int n,m;
ll dp[maxn];
int Next[maxn];
void getNext()
{
    int k = -1;
    int j = 0;
    Next[0] = -1;
    while(j<m)
    {
        if(k == -1||p[k]==p[j])
        {
            k++;
            j++;
            Next[j] = k;
        }
        else k = Next[k];
    }
}
void kmp()
{
    getNext();
    int i = 0;
    int j = 0;
    dp[0] = 1;
    while(i<n)
    {
        if(j == -1||t[i]==p[j])
        {
            i++;
            dp[i] = dp[i-1];
            j++;
        }
        else j = Next[j];
        if(j == m)
        {
            if(i>m) dp[i] = (dp[i]+dp[i-m])%mod;
            if(i==m) dp[i] = 2;
            j = Next[j];
        }
    }
}
int main()
{
    int T,kase = 0;cin>>T;
    while(T--)
    {
        scanf("%s",t);
        scanf("%s",p);
        n = strlen(t);
        m = strlen(p);
        kmp();
        printf("Case #%d: ",++kase);
        printf("%d
",dp[n]);
    }
    return 0;
}
卷珠帘

HDU5764 After a Sleepless Night

HDU5765 Bonds

HDU5766 Filling

HDU5768 Lucky7

HDU5769 Substring

HDU5770 Treasure

HDU5771 Turn Game

HDU5772 String problem

HDU5773 The All-purpose Zero

这两天真好修炼dp,看完这题感觉和最长上升子序列有点像,就去学O(nlogn)的LIS了,老柴也套了个板子,他改的那一下子我感觉特别巧妙,真服。

对于是非0的元素,直接像普通的LIS那样处理。

对于0的元素

cnt[++len] = cnt[len-1]+1;
for(int i=len-1;i>1;i--)
{
cnt[i] = cnt[i-1]+1;
}
cnt[1] = 0;

举一个例子,比如cnt中现在是1 5 9,这时候a[i]是0,这样处理完后是,0,2,6,10.那么在下一次a[i]非0时,比如a[i]=7,那么这时候插进来,

得序列0,2,6,7;前面的那个a[i]=0仍然起了增一的作用,即相当于当时使得cnt[3] = 6,插入任何数字那个0都起了作用。

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
int cnt[maxn];
int main()
{
    int t,n,kase = 0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int len = 1;
        cnt[len] = a[1];
        for(int i=2;i<=n;i++)
        {
            if(a[i])
            {
                if(a[i]>cnt[len])
                {
                    cnt[++len] = a[i];
                }
                else
                {
                    int pos = lower_bound(cnt+1,cnt+len+1,a[i])-cnt;//非递减序列中返回第一个大于等于a[i]的位置
                    cnt[pos] = a[i];
                }
            }
            else
            {
                cnt[++len] = cnt[len-1]+1;
                for(int i=len-1;i>1;i--)
                {
                    cnt[i] = cnt[i-1]+1;
                }
                cnt[1] = 0;
            }
        }
        printf("Case #%d: %d
",++kase,len);
    }
    return 0;
}
卷珠帘

HDU5774 Where Amazing Happens

这道大水题,刚开始我问老柴这题是什么意思,他说就是统计每个人名出现的次数,我说好那不就数一数就行了嘛,他说对呀,我就开始数,结果数了七八个,我就晕了,这人名,好多很难区分啊,机智的我开始用map统计了。o(^▽^)o

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <cstring>
using namespace std;
map<char*,int> mp;
void pre()
{
    mp["Baltimore Bullets"] = 1;
    mp["Boston Celtics"] = 17;
    mp["Chicago Bulls"] = 6;
    mp["Cleveland Cavaliers"] = 1;
    mp["Dallas Mavericks"] = 1;
    mp["Detroit Pistons"] = 3;
    mp["Golden State Warriors"] = 2;
    mp["Houston Rockets"] = 2;
    mp["L.A. Lakers"] = 11;
    mp["Miami Heat"] = 3;
    mp["Milwaukee Bucks"] = 1;
    mp["Minneapolis Lakers"] = 5;
    mp["New York Knicks"] = 2;
    mp["Philadelphia 76ers"] = 2;
    mp["Philadelphia Warriors"] = 2;
    mp["Portland Trail Blazers"] = 1;
    mp["Rochester Royals"] = 1;
    mp["San Antonio Spurs"] = 5;
    mp["Seattle Sonics"] = 1;
    mp["St. Louis Hawks"] = 1;
    mp["Syracuse Nats"] = 1;
    mp["Washington Bullets"] = 1;

}
int main()
{
    pre();
    int t,kase = 0;
    scanf("%d",&t);
    getchar();
    char ss[1004];
    while(t--)
    {
        int flag = 0;
        gets(ss);
        map<char*,int>::iterator it;
        printf("Case #%d: ",++kase);
        for(it = mp.begin(); it!=mp.end();it++)
        {
            if(strcmp(it->first,ss)==0)
            {
                flag =  1;
                printf("%d
",it->second);
                break;
            }
        }
        if(!flag) printf("0
");
    }
    return 0;
}
卷珠帘

HDU5775 Bubble Sort

一道统计逆序对的题,和曾经的线段树模板题很像,正好现在再复习下。

线段树模板:

#include <iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
struct node{
    int l,r,v;
};
node T[maxn*4];
void build(int l,int r,int q){
    T[q].l = l;
    T[q].r = r;
    if(T[q].l == T[q].r){
        T[q].v = 0;
        return;
    }
    int mid = (l+r)/2;
    build(l,mid,q*2);
    build(mid+1,r,q*2+1);
    T[q].v = T[q*2].v + T[q*2+1].v;
}
int sum(int l,int r,int q){
    if(l<=T[q].l&&T[q].r<=r){
        return T[q].v;
    }
    int mid = (T[q].l+T[q].r)/2;
    if(r<=mid) return sum(l,r,q*2);
    if(l>mid) return sum(l,r,q*2+1);
    return sum(l,mid,q*2)+sum(mid+1,r,q*2+1);
}
void Add(int i,int j,int q){
    if(T[q].l == T[q].r){
        T[q].v += j;
        return;
    }
    int mid = (T[q].l+T[q].r)/2;
    if(i<=mid) Add(i,j,q*2);
    else Add(i,j,q*2+1);
    T[q].v = T[q*2].v+T[q*2+1].v;
}
void input(){
    int T,n,kase = 0,x,w;
    cin>>T;
    while(T--){
        scanf("%d",&n);
        build(1,n,1);
        int ans = 0;
        for(int i = 1; i<=n; i++)
        {
            scanf("%d",&x);
            w = x-i+sum(x,n,1);
            a[x] = max(i+w,x)-min(x,i);
            Add(x,1,1);
        }
        printf("Case #%d:",++kase);
        for(int i = 1; i<=n; i++)
        {
            printf(" %d",a[i]);
        }
        printf("
");
    }
}
int main()
{
    input();
    return 0;
}
卷珠帘

树状数组模板:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <cstring>
using namespace std;
# include <stdio.h>
# include <string.h>
const int maxn = 1e5+5;
int a[maxn], b[maxn], n;
void u(int v){while (v) b[v]++, v -= v & -v;}


int q(int v){
    int x = 0;
    while (v <= n) x += b[v], v += v & -v;
    return x;
}


int main()
{
    int t,kase = 0;
    scanf("%d",&t);
    while(t--)
    {
        memset (b, 0, sizeof(b)) ;
        scanf("%d",&n);
        int x,w;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            w = x-i+q(x);
            a[x] = max(i+w,x)-min(x,i);
            u(x);
        }
        printf("Case #%d:",++kase);
        for(int i=1;i<=n;i++)
            printf(" %d",a[i]);
        printf("
");
    }
    return 0;
}
卷珠帘
原文地址:https://www.cnblogs.com/littlepear/p/5716000.html