官方题解: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个操作,求能满足条件的操作顺序
我们只要每次找一行或一列颜色除了0都相同的,然后如果有对应的操作,就把这行这列都赋值成0即可
答案只与终态有关。
#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;
}