2015 Multi-University Training Contest 8

官方题解:2015 Multi-University Training Contest 8 solutions BY 绍兴一中




1005 Danganronpa

HDU 5384:http://acm.hdu.edu.cn/showproblem.php?pid=5384

题意:给n个字符串\(A_i\),m个字符串\(B_j\),定义\(f(A,B)\)为B字符串在A字符串中出现的次数,可以重叠。求对于每个\(A_i\),\(\sum_{j=1}^{m}f(A_i,B_j)\)的值



AC自动机。


用所有\(B_j\)字符串构造一个AC自动机

对于每个\(A_i\)扫描一次即可求出答案


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<map>
using namespace std;
#define pb push_back
#define LL __int64
#define N 100005
#define INF 1<<30

struct node {
    node *fail;
    node *next[26];
    int count;
    node() {
        fail=NULL;
        count=0;
        for(int i=0; i<26; ++i) {
            next[i]=NULL;
        }
    }
};

node *root;
queue<node*> q;

void insert(string str) {
    int k,len;
    int i;
    node *p=root;
    len=str.size();
    for(i=0; i<len; ++i) {
        k=str[i]-'a';
        if(p->next[k]==NULL)
            p->next[k]=new node();
        p=p->next[k];
    }
    p->count++;
}

void build_ac() {   //初始化fail指针,BFS
    node *now,*nex;
    int i,j;
    while(!q.empty())q.pop();
    q.push(root);
    while(!q.empty()) {
        now=q.front();
        q.pop();
        nex=NULL;
        for(i=0; i<26; ++i) {
            if(now->next[i]!=NULL) {
                if(now==root) {         //第一个元素fail必指向根
                    now->next[i]->fail=root;
                } else {
                    nex=now->fail;        //失败指针
                    while(nex!=NULL) {      //2种情况结束:匹配为空or找到匹配
                        if(nex->next[i]!=NULL) {     //找到匹配
                            now->next[i]->fail=nex->next[i];
                            break;
                        }
                        nex=nex->fail;
                    }
                    if(nex==NULL)   //为空则从头匹配
                        now->next[i]->fail=root;
                }
                q.push(now->next[i]);
            }
        }
    }
}


int query(string txt) {
    int k,len,res;
    node *p=root;
    res=0;
    len=txt.size();
    for(int i=0; i<len; ++i) {
        k=txt[i]-'a';
        while(p->next[k]==NULL&&p!=root)  //跳转失败指针
            p=p->fail;
        p=p->next[k];
        if(p==NULL)
            p=root;
        node *temp=p;   //p不动,temp计算后缀串
        while(temp!=root) { //&&temp->count!=-1){
            res+=temp->count;
            //temp->count=-1;//重复不再计算
            temp=temp->fail;
        }
    }
    return res;
}

string a[N],b;


int main() {
    //freopen("C:\\Users\\F\\Desktop\\in.txt", "r", stdin);
    //freopen("C:\\Users\\F\\Desktop\\out.txt", "w", stdout);
    //std::ios::sync_with_stdio(false);
    int n,m;
    int T;
    cin>>T;
    while(T--) {
        root=new node();
        cin>>n>>m;
        for(int i=1; i<=n; ++i)
            cin>>a[i];
        for(int i=1; i<=m; ++i) {
            cin>>b;
            insert(b);
        }
        build_ac();

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


1007 Cover

HDU 5386:http://acm.hdu.edu.cn/showproblem.php?pid=5386

题意:有一个n*m的矩阵,定义两种操作:

L x y: for(int i=1;i<=n;i++)color[i][x]=y;
H x y:for(int i=1;i<=n;i++)color[x][i]=y;

现在给出矩阵的初始状态和终态,以及m个操作,求能满足条件的操作顺序


我们只要每次找一行或一列颜色除了00都相同的,然后如果有对应的操作,就把这行这列都赋值成00即可

答案只与终态有关。


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<map>
using namespace std;
#define pb push_back
#define LL __int64
#define N 505
#define INF 1<<30

int dir[N],x[N],y[N];
int mpt[N][N];
int vis[N];
int ans[N];
int n,m;
int cle=0;

int judge(int d,int opx,int opy) {
    for(int i=1; i<=m; ++i)
        if(!vis[i]&&((dir[i]==d&&x[i]==opx&&(y[i]==opy||opy==0))||cle)) {
            vis[i]=1;
            return i;
        }
    return 0;
}

void change(int id) {
    if(dir[id]==1)
        for(int i=1; i<=n; i++)mpt[i][x[id]]=0;
    if(dir[id]==2)
        for(int i=1; i<=n; i++)mpt[x[id]][i]=0;
}

int main() {
    //freopen("C:\\Users\\F\\Desktop\\in.txt", "r", stdin);
    //freopen("C:\\Users\\F\\Desktop\\out.txt", "w", stdout);
    int T;

    scanf("%d",&T);
    while(T--) {
        cle=0;
        memset(vis,0,sizeof(vis));
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=n; ++j)
                scanf("%d",&mpt[i][j]);


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

        char op[2];
        for(int i=1; i<=m; ++i) {
            scanf("%s%d%d",op,&x[i],&y[i]);
            if(op[0]=='L')dir[i]=1;
            else dir[i]=2;
        }

        int k=m;
        int opy;
        int id;
        while(k) {
            int flag=0;
            id=0;

            for(int i=1; i<=n&&!flag; ++i) {
                int same=1;
                opy=mpt[1][i];
                for(int j=2; j<=n&&same; ++j) {
                    if(mpt[j][i]!=opy&&mpt[j][i]!=0&&opy!=0) same=0;
                    opy=max(opy,mpt[j][i]);
                }
                if(same) {
                    id=judge(1,i,opy);
                    flag=(id?1:0);
                }
            }
            for(int i=1; i<=n&&!flag; ++i) {
                int same=1;
                opy=mpt[i][1];
                for(int j=2; j<=n&&same; ++j) {
                    if(mpt[i][j]!=opy&&mpt[i][j]!=0&&opy!=0) same=0;
                    opy=max(opy,mpt[i][j]);
                }

                if(same) {
                    id=judge(2,i,opy);
                    flag=(id?1:0);
                }
            }
            ans[k--]=id;
            change(id);
            flag=1;
            for(int i=1; i<=n&&flag; ++i)
                for(int j=1; j<=n&&flag; ++j)
                    if(mpt[i][j]!=0)flag=0;
            if(flag)cle=1;
        }

        for(int i=1; i<=m; ++i) {
            if(i!=1)printf(" ");
            printf("%d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}


1008 Clock

HDU 5387:http://acm.hdu.edu.cn/showproblem.php?pid=5387

题意:给一个时刻,分别求时针与分针,时针与秒针,分针与秒针的角度


计算一下角度就行,注意分数以及钝角的处理


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<map>
using namespace std;
#define pb push_back
#define LL __int64
#define N 100005
#define INF 1<<30

int gcd(int a,int b) {
    return (b>0)?gcd(b,a%b):a;
}

int main() {
    //freopen("C:\\Users\\F\\Desktop\\in.txt", "r", stdin);
    //freopen("C:\\Users\\F\\Desktop\\out.txt", "w", stdout);
    int ans[4];
    int T;
    int h,m,s;
    int hh,mm,ss;
    scanf("%d",&T);
    while(T--) {
        scanf("%d:%d:%d",&h,&m,&s);
        if(h>=12)h-=12;
        hh=h*30*3600+m*30*60+s*30;
        mm=m*6*3600+s*6*60;
        ss=s*6*3600;
        ans[1]=abs(hh-mm);
        ans[2]=abs(hh-ss);
        ans[3]=abs(mm-ss);

        for(int i=1; i<=3; ++i) {
            ans[i]=min(360*3600-ans[i],ans[i]);
            if(ans[i]%3600==0)
                printf("%d",ans[i]/3600);
            else {
                printf("%d/%d",ans[i]/gcd(ans[i],3600),3600/gcd(ans[i],3600));
            }
            printf(" ");
        }
        printf("\n");
    }
    return 0;
}


1010 Zero Escape

HDU 5389:http://acm.hdu.edu.cn/showproblem.php?pid=5389

题意:n个人,每人有一个标识符\(id_i\)(可以相同),现有两道门编号为\(A,B\)(可以相同)。对每个人要选择一个门通过。最后选择同一个门的人的标识符之和的数根和门的编号相同就可以通过。求能让所有人通过的方案数。


DP


定义\(dr(x)\)为\(x\)的数根

数根有一个性质:\(dr(a+b) \equiv dr(a)+dr(b)\space (\mod 9\space )\)

所以可以用\(dp[i][j]\)表示从前\(i\)个数中选出一些数使其和的树根对9取模为\(j\)的方案数有多少

状态转移方程:

\(for \ each \  j \in [0,9]\)

\(\qquad dp[i][j]=dp[i-1][j]\)
\(\qquad dp[i][(j+d[i]) \% 9]=dp[i][(j+d[i]) \% 9]+dp[i-1][j]\)


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<string>
#include<map>
using namespace std;
#define pb push_back
#define LL __int64
#define N 100005
#define INF 1<<30
#define MOD 258280327

int n;
int d[N];
LL dp[N][10];
int sum;
int A,B;

int main(){
    //freopen("C:\\Users\\F\\Desktop\\in.txt", "r", stdin);
    //freopen("C:\\Users\\F\\Desktop\\out.txt", "w", stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        sum=0;
        memset(dp,0,sizeof(dp));
        scanf("%d%d%d",&n,&A,&B);
        for(int i=1;i<=n;++i){
            scanf("%d",&d[i]);
            sum+=d[i];
        }
        sum%=9;
        dp[1][d[1]]=1;
        dp[1][0]=1;
        for(int i=2;i<=n;++i){
            for(int j=0;j<=9;++j){
                dp[i][j]=(dp[i][j]+dp[i-1][j])%MOD;
                dp[i][(j+d[i])%9]=(dp[i][(j+d[i])%9]+dp[i-1][j])%MOD;
            }
        }
        LL ans=0;
        if((A+B)%9==sum)ans=dp[n][A%9];
        if(ans==0&&(sum%9==A%9||sum%9==B%9))ans++;
        printf("%I64d\n",ans%MOD);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值