[GD_SGOI 数字贴吧]AC自动机上的动态规划、高精度

【题目】

提交文件:num.pas/.cpp

输入文件:num.in

输出文件:num.out

 

在百度贴吧里输入关键词,就可以进入相应的贴吧参与该话题的讨论。然而,有一些关键词是不能作为贴吧名称的。我们把这些关键词称为敏感词。

如果一个字符串的任何子串(包括本身)都不是敏感词,那么这个字符串才可以作为贴吧名称。

有一些贴吧是直接用数字作为贴吧名称的。例如:“0”、“1”、“100”、“0123456789”。然而,数字里面也有敏感词。如果“00”是敏感词,则包含连续2个“0”的数字都不得作为贴吧名称。

你的任务是统计一下一共有多少个数字可以作为贴吧名称。

 

输入格式:

第一行是,表示贴吧名称的最大长度为N≤63),一共有M≤10)个数字是敏感词。

以下M行每行一个数字,长度都不超过5。

 

输出格式:

输出可以作为贴吧名称的数字个数。

 

输入样例:

3 10

2

3

4

5

6

7

8

9

10

11

 

输出样例:

6

 

样例解释:

只有“0”、“1”、“00”、“01”、“000”、“001”可以作为贴吧名称。

【算法分析】
很容易发现。就是AC自动机dp,貌似又叫DFA?
然后对给出的串建立AC自动机。
于是F[i][j]表示已建立的串长度为i,在自动机中的j状态,所能有的方案数。
然后转移的话,就是在自动机的每个状态那里,加一个0~9的字符,然后给“转移以后的状态”加上当前这个状态的方案数。(加法原理。。。)
但是“推出方案数”的状态不能是一个串的结尾,因为按题意不能匹配!

最后的答案就是所有F[i][j]之和。当然,j不能是一个串的结尾。
最后由于这题实在太不厚道,还要用高进度写F[i][j]。。。
太猥琐了。。。
【CODE】
#include #include #include const int N=55;
int n,m,tot,Q[N];
struct Trie_t{int son[10];int Final,next;}Tr[N];
int F[70][55][101];
char str[N];

void Insert(){
int L=strlen(str);
int p=1;
for (int i=0;i if (Tr[p].son[str[i]-‘0’]) p=Tr[p].son[str[i]-‘0’];
else{
tot++;
Tr[p].son[str[i]-‘0’]=tot;
p=tot;
}
Tr[p].Final++;
}

void init(){
tot=1;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%s",str);
Insert();
}
}

void makenext(){
int head=0,tail=1;
Q[1]=1;
while (head!=tail){
head++;
int &p=Q[head];
if (Tr[Tr[p].next].Final) Tr[p].Final=1;
for (int c=0;c<10;c++)
if (Tr[p].son){
int q=Tr[p].next;
while (q && !Tr[q].son) q=Tr[q].next;
if (q) Tr[Tr[p].son].next=Tr[q].son;
else Tr[Tr[p].son].next=1;
Q[++tail]=Tr[p].son;
}
}
}

void plus(int *A,int *B){
for (int i=1;i<=100;i++) A[i]+=B[i];
for (int i=100;i>1;i–){
A[i-1]+=A[i]/10;
A[i]%=10;
}
}

void output(int *A){
int i,j;
for (i=1;i<=100;i++)
if (A[i]){
for (j=i;j<=100;j++) printf("%d",A[j]);
printf("n");
return;
}
printf("0n");
}

void dp(){
memset(F,0,sizeof(F));
F[0][1][100]=1;
for (int L=0;L for (int i=1;i<=tot;i++) if (!Tr[i].Final)
for (int j=0;j<10;j++){
int p=i;
while (p && !Tr[p].son[j]) p=Tr[p].next;
if (!p) plus(F[L+1][1],F[L][i]);
else plus(F[L+1][Tr[p].son[j]],F[L][i]);
}
int ans[101];
memset(ans,0,sizeof(ans));
for (int i=1;i<=n;i++)
for (int j=1;j<=tot;j++) if (!Tr[j].Final)
plus(ans,F[i][j]);
output(ans);
}

int main(){
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);
init();
makenext();
dp();
}

[GD_SGOI 三角阵]找规律、字符串变换

【题目】

提交文件:tri.pas/.cpp

输入文件:tri.in

输出文件:tri.out

 

  把3个相同的小三角形按如下方式连接起来就形成了一个一级三角阵。

  我们把位于顶端的小三角形标记为T,位于左端的小三角形标记为L,位于右端的小三角形标记为R。

  把3个一级三角阵按同样的方式连接起来就形成了一个二级三角阵。

  我们为顶端的三角阵的标记添加前缀T,为左端的三角阵的标记添加前缀L,为右端的三角阵的标记添加前缀R。

把3个二级三角阵按同样的方式连接起来就形成了一个三级三角阵。

同样地为顶端的三角阵的标记添加前缀T,为左端的三角阵的标记添加前缀L,为右端的三角阵的标记添加前缀R。

依次类推,可以构建一个N级三角阵。

如果两个小三角形有公共点,则认为这两个小三角形相邻,可以一步到达。

你的任务是求从一个小三角形走到另一个小三角形至少需要多少步。

 

输入格式:

第一行是三角阵的等级≤30)。

第二行和第三行都是一个长度为N的字符串,由大写字母“L”、“R”、“T”组成,表示两个小三角形的标号。

 

输出格式:

输出一个数,表示从一个小三角形走到另一个小三角形所需的最少步数。

 

输入样例:

3

TRL

RLR

 

输出样例:

5

 

样例解释:

从“TRL”出发,途经“TRR”、“RTT”、“RTL”、“RLT”,最后到达“RLR”,一共需要5步。

【算法分析】
首先我们围观它走一步的话,那个字符串会有什么变化。
第一种可能:"abc"->"abk" 然后这个这个c和k是可以任意的。
第二种可能:"xyzabbbbbbb…bbbbb"->"xyzbaaaaaaa…aaaaa",当然,这个a和b不能相同。
于是可以很直接地得到一个走到本三角顶点的算法。
那就是从高位到低位,一位一位地处理,具体自己YY吧,不难。
并且容易发现他是最优的。(怎么发现?看图。。。)
然后对于A串和B串,我们先把相同的前缀删掉。
对于剩下的东西而言。有两种可能。
剩下的设长度为n。
1、从我所属于的这个(n-1)级三角形直接走到对象的那个(n-1)级三角形。
2、从我所属于的这个(n-1)级三角形绕路另一个(n-1)级三角形,再跑到对象的那个(n-1)三角形。
于是分开两种情况,min一下即可。
【CODE】
#include #include #include typedef __int64 lld;
const int N=55;
int n;
char str1[N],str2[N],s1[N],s2[N];

lld solve(char *ss1,char *ss2){
memcpy(s1,ss1,sizeof(s1));
memcpy(s2,ss2,sizeof(s2));
int i,j,k;
lld ans=0;
for (i=1;i<=n;i++)
if (s1[i]!=s2[i]){
for (j=n;j>i;j–)
if (s2[i]!=s1[j]){
ans+=(lld)(1)<<(n-j);
s1[j]=s2[i];
}
ans++;
for (j=n;j>i;j–) s1[j]=s1[i];
s1[i]=s2[i];
}
return ans;
}

void work(){
if (strcmp(str1+1,str2+1)==0) puts("0");
else{
lld ans=solve(str1,str2);
char str[N];
memcpy(str,str2,sizeof(str));
int i=1,j;
while (str1[i]==str2[i]) i++;
if (str1[i]==’T’){
if (str2[i]==’L’) str[i]=’R’;
if (str2[i]==’R’) str[i]=’L’;
}
if (str1[i]==’L’){
if (str2[i]==’T’) str[i]=’R’;
if (str2[i]==’R’) str[i]=’T’;
}
if (str1[i]==’R’){
if (str2[i]==’L’) str[i]=’T’;
if (str2[i]==’T’) str[i]=’L’;
}
for (j=i+1;j<=n;j++) str[j]=str1[i];
lld now=solve(str1,str)+solve(str,str2);
if (now printf("%I64dn",ans);
}
}

int main(){
freopen("tri.in","r",stdin);
freopen("tri.out","w",stdout);
scanf("%d%s%s",&n,str1+1,str2+1);
work();
return 0;
}

[POI2005]Kos-Dicing【二分答案+最大流】

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1532
【算法分析】
二分答案然后,将比赛分配给选手,判定能不能分完。
这个用最大流实现。
【其它】sap被卡。HLPP被卡。。。于是dinic。。。
于是你可以见识到什么叫卡过。。。
34 30276 edward_mj 3164K 4772MS G++ 2.39K 2010-06-16 01:39:26 【CODE】
#include #include #include #define min(x,y) ((x)<(y)?(x):(y))
const int N=20005;
const int INF=0x7FFFFFFF;
struct edge{int x,y,c;edge *next,*op;}*ls[N],*cur[N],*fa[N];
int n,m,vs,vt,Flow;
int d[N],lx[N],ly[N],v[N],Q[N];

void init(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d",&lx[i],&ly[i]);
vs=0; vt=n+m+1;
}

inline void addedge(int x,int y,int c){
edge *p=(edge*)malloc(sizeof(edge));
edge *q=(edge*)malloc(sizeof(edge));
p->x=x; p->y=y; p->c=c; p->next=ls[x]; ls[x]=p; p->op=q;
q->x=y; q->y=x; q->c=0; q->next=ls[y]; ls[y]=q; q->op=p;
}

bool bfs(){
for (int i=vs;i<=vt;i++){
d[i]=INF;
v[i]=0;
cur[i]=ls[i];
}
d[vs]=1; Q[1]=vs;
for (int st=1,ed=1;st<=ed;st++)
for (edge *t=ls[Q[st]];t;t=t->next)
if (t->c && d[t->y]==INF){
d[t->y]=d[t->x]+1;
Q[++ed]=t->y;
if (t->y==vt) return true;
}
return false;
}

void change(){
int nf=INF;
for (int i=vt;i!=vs;i=fa[i]->x)
if (fa[i]->c for (int i=vt;i!=vs;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}
Flow+=nf;
}

void dinic(){
int i;
while (bfs()){
i=vs;
while (cur[vs]){
edge *&t=cur[i];
for (;t;t=t->next)
if (t->c && d[t->x]+1==d[t->y] && !v[t->y]) break;
if (t){
fa[t->y]=t;
i=t->y;
if (i==vt){
change();
i=vs;
}
}else{
v[i]=1;
if (i!=vs) i=fa[i]->x;
}
}
}
}

bool Can(int Limit){
for (int i=vs;i<=vt;i++)
while (ls[i]){
edge*t=ls[i];
ls[i]=t->next;
free(t);
}
for (int i=1;i<=m;i++){
addedge(vs,i,1);
addedge(i,m+lx[i],1);
addedge(i,m+ly[i],1);
}
for (int i=1;i<=n;i++) addedge(m+i,vt,Limit);
Flow=0;
dinic();
if (Flow==m) return true;
return false;
}

void solve(){
int l=m/n,r=m,mid;
while (l mid=(l+r)/2;
if (Can(mid)) r=mid;
else l=mid+1;
}
printf("%dn",l);
}

int main(){
init();
solve();
}

[ZOJ3348 Schedule]网络流

【题目大意】
知道之前的比赛结果,和将要打的对阵。问DD有没有可能得冠军?(不能是并列冠军)
【算法分析】
很直观的想法,剩余的对阵里,如果有DD,那么让DD赢。
否则设为未群定比赛。
然后最后构一个二分图,把比赛分给那些人。
那些人还能剩多少场允许胜,那么就连这么多容量的边。
大概这样。
【其它】。。。之前WA成SB,重写1Y。泪流满面。
居然跑了个第一,照个相,不然马上被刷下来了。

【CODE】
#include #include #include #define clr(a) memset(a,0,sizeof(a))
const int maxn=55;
const int N=20005;
struct edge{int x,y,c;edge *next,*op;};
struct Network_t{
edge *ls[N],*cur[N],*fa[N];
int d[N],gap[N],Flow,S,T;
void init(int n){
for (int i=0;i<=n;i++)
while (ls[i]){
edge *t=ls[i];
ls[i]=t->next;
free(t);
}
}

void addedge(int x,int y,int c){
edge *p=(edge*)malloc(sizeof(edge));
edge *q=(edge*)malloc(sizeof(edge));
p->x=x; p->y=y; p->c=c; p->next=ls[x]; ls[x]=p; p->op=q;
q->x=y; q->y=x; q->c=0; q->next=ls[y]; ls[y]=q; q->op=p;
}

void relabel(int p,int &n){
cur[p]=ls[p];
int mm=n;
for (edge *t=ls[p];t;t=t->next)
if (t->c && d[t->y] d[p]=mm+1;
}

void change(){
int NF=0x7FFFFFFF;
for (int i=T;i!=S;i=fa[i]->x)
if (fa[i]->c Flow+=NF;
for (int i=T;i!=S;i=fa[i]->x){
fa[i]->c-=NF;
fa[i]->op->c+=NF;
}
}

void sap(int n){
int i;
Flow=0;
for (i=0;i<=n;i++){
cur[i]=ls[i];
d[i]=0;
gap[i]=0;
}
gap[0]=n+1;
i=S;
while (d[S]<=n){
edge *&t=cur[i];
for (;t;t=t->next)
if (t->c && d[t->y]+1==d[t->x]) break;
if (t){
fa[t->y]=t;
i=t->y;
if (i==T){
change();
i=S;
}
}else{
if (!–gap[d[i]]) return;
relabel(i,n);
gap[d[i]]++;
if (i!=S) i=fa[i]->x;
}
}
}
}Network;

int n,m,cnt,NL;
int win[maxn];
char Name[maxn][22];

int Get(char *str){
int i,j;
bool flag;
for (i=1;i<=n;i++){
flag=true;
for (j=0;j<22;j++)
if (str[j]!=Name[i][j]){
flag=false;
break;
}
if (flag) return i;
}
n++;
for (j=0;j<22;j++) Name[n][j]=str[j];
return n;
}

void init(){
clr(Name);
clr(win);
Network.init(N-1);
char s1[22],s2[22],s3[22];
n=1; Name[1][0]=Name[1][1]=’D’;
for (int x,y,i=1;i<=m;i++){
clr(s1); clr(s2); clr(s3);
scanf("%s%s%s",s1,s2,s3);
x=Get(s1);
y=Get(s2);
if (s3[0]==’w’) win[x]++;
else win[y]++;
}

Network.S=0; Network.T=1; cnt=0;
scanf("%d",&m);
for (int x,y,i=1;i<=m;i++){
clr(s1); clr(s2);
scanf("%s%s",s1,s2);
x=Get(s1);
y=Get(s2);
if (x==1) win[1]++;
if (y==1) win[1]++;
if (x!=1 && y!=1){
cnt++;
Network.addedge(x,NL+cnt,1);
Network.addedge(y,NL+cnt,1);
Network.addedge(NL+cnt,Network.T,1);
}
}
}

bool solve(){
for (int i=2;i<=n;i++)
if (win[i]>=win[1]) return false;
for (int i=2;i<=n;i++)
Network.addedge(Network.S,i,win[1]-win[i]-1);
Network.sap(NL+cnt);
if (Network.Flow==cnt) return true;
return false;
}

int main(){
clr(Network.ls);
while (scanf("%d%d",&NL,&m)!=EOF){
init();
if (solve()) puts("Yes");
else puts("No");
}
}

[ZOJ3334 Body Check]GXX神牛的美妙解法:解不等式

【题目大意】
给定n个工作,m条通道。
然后这m条通道要么全部一起完成工作,要么只有1条在完成工作,求把n个工作全部做完至少需要多长时间。
【算法分析】
http://hi.baidu.com/pojoflcc/blog/item/a5e757816511ccdb9123d9b4.html
Lcc大神也写了解题报告,但是他的方法思路过于繁杂。。。虽然也可以做到排序的复杂度。
实际上,我们就是要让n个工作的并行时间尽可能地长。

姑且设这个并行时间为ans。
本题容易得到一个二分ans的算法。
具体是:
先二分ans。
然后将大于ans的工作时间置为ans。最后判定所有工作时间的和是否>=ans*m
是,则可行,否则不可行。
(想象你在填一个m*ans的表,就很容易理解)

然后现在根据GXX神牛的指示。
实际上不需要二分答案。

我们现在来看。假设我们将工作时间Ai已经按降序排列。
并再次假设A[i]<=ans<=A[i+1]
那么类似二分的判定的地方得到一个不等式
S[i+1,n]+i*ans>=m*ans     (S[i,j]表示从a[i]到a[j]的和)
于是我们整理一下,得出3个不等式:
ans>=A[i]           ——————①
ans<=A[i+1]       ——————②
ans<=S/(m-i)     ——————③
然后解它们看在这个区间有无解就行了。
可能大家觉得第③式有点问题。就是那个(m-i)的正负不知道,所以不等式不知道要不要变号。
实际上,在i递增到m=i时,必然就有解,所以不会出现m于是问题得到完美解决。
【时间复杂度】排序O(n lg n)+处理O(n)
【空间复杂度】O(n)
【其它】
1、深深地YM GXX神牛
2、注意到那个二分的解法,本题精度要求是1e-9,所以很容易挂。。。当然也是有可能AC的。
【CODE】
#include #include #include #define AA (*((double*)A))
#define BB (*((double*)B))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
const int N=1005;
int m,n;
double a[N],s[N],ans,x;

void solve(){
ans=0;
a[n+1]=s[n+1]=0;
a[0]=1e20;
for (int i=n;i>=1;i–) s[i]=s[i+1]+a[i];
for (int i=0;i<=n;i++)
if (i==m){
ans=a[i];
break;
}else
if (m>i){
x=min(a[i],s[i+1]/(m-i));
if (x>=a[i+1]){
ans=x;
break;
}
}
printf("%.9lfn",s[1]-ans*m+ans);
}

int cmp(const void *A,const void *B){
if (AA if (AA>BB) return -1;
return 0;
}

int main(){
while (scanf("%d%d",&m,&n)!=EOF){
for (int i=1;i<=n;i++) scanf("%lf",&a[i]);
qsort(a+1,n,sizeof(double),cmp);
solve();
}
}