A - 第一章:小试牛刀
KMP板子
#include <iostream>
#include <cstring>
using namespace std;
const int N=1e6+1;
int i,j,ls,lp,nxt[N];
string s,p;
int main()
{
ios::sync_with_stdio(false);
cin>>s>>p;
ls=s.length();
lp=p.length();
i=0,j=-1;
nxt[0]=-1;
while(i<lp)
if(j<0||p[i]==p[j])
nxt[++i]=++j;
else
j=nxt[j];
i=0,j=0;
while (i<ls){
if (j<0||s[i]==p[j])
i++,j++;
else
j=nxt[j];
if (j==lp){
cout<<i-lp+1<<" ";
j=nxt[j];
}
}
}
B - 第二章:北境之王的现身
(f[i][j])表示前(i)个字符组成的串匹配到KMP自动机的(j)号节点的方案数
每一步对答案的贡献就是(f[i][n]*sum^n_{i+1}|t[i]|)
#include <cstdio>
#include <cstring>
typedef long long ll;
const ll N=1e3+1,M=26,T=2e6+2,K=1e9+7;
char s[N],t[M];
ll ls,lt[T],f[N],g[N],tr[N][M],cnt[N],fail[N],m,ans,bak[T];
void build(char *s){
ls=strlen(s);
for (ll i=0;i<ls;i++)
tr[i][s[i]-'a']=i+1;
for (ll i=1;i<=ls;i++)
for (ll j=0;j<M;j++)
if (tr[i][j])
fail[tr[i][j]]=tr[fail[i]][j];
else
tr[i][j]=tr[fail[i]][j];
}
int main(){
scanf("%s",s);
build(s);
scanf("%lld",&m);
for (ll i=1;i<=m;i++) scanf("%lld",<[i]);
bak[m+1]=1;
for (ll i=m;i>=1;i--) bak[i]=bak[i+1]*lt[i]%K;
f[0]=1;
for (ll i=1;i<=m;i++){
memset(g,0,sizeof(g));
scanf("%s",t);
for (ll j=0;j<lt[i];j++){
for (ll k=0;k<=ls;k++)
g[tr[k][t[j]-'a']]=(g[tr[k][t[j]-'a']]+f[k])%K;
}
ans=(ans+g[ls]*bak[i+1]%K)%K;
memcpy(f,g,sizeof(f));
}
printf("%lld",ans);
}
C - 第三章:向监狱进发
每个串的所有前缀都可以由(fail)指针得到,所以可以由(fail)指针构建一个树,再按拓扑序(dp)即可
(f[i][j])表示拓扑序前(i)个点构成了(j)个集合的方案数,(w(i))表示前(i-1)个点中有多少个第(i)点的祖先,可以用树状数组维护
(f[i][j]=f[i-1][j]*(j-w(i))+f[i-1][j-1])
#include <iostream>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const ll N=1e5+10,M=26,K=1e9+7;
ll tr[N][26],tot,fail[N];
ll k,m,q,n,a,b,sz,in[N],out[N],t[N],f[N][301],tmp[N],head[N];
string s[N];
vector<int> pos[N];
struct E{
ll next,to;
}e[N];
void add(ll x,ll v){
for (ll i=x;i<=tot;i+=i&-i) t[i]+=v;
}
ll query(ll x){
ll ans=0;
for (ll i=x;i;i-=i&-i) ans+=t[i];
return ans;
}
void insert(ll a,ll b){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
}
ll inserts(char *s,ll id){
ll u = 0;
pos[id].push_back(0);
for (ll i = 0; s[i]; i++) {
if (!tr[u][s[i]-'a'])
tr[u][s[i]-'a']=++tot;
u=tr[u][s[i]-'a'];
pos[id].push_back(u);
}
return u;
}
queue<int> que;
void dfs(ll x){
in[x]=++tot;
for (ll i=head[x];i;i=e[i].next){
ll u=e[i].to;
dfs(u);
}
out[in[x]]=tot;
}
void build(){
for (ll i=0;i<M;i++)
if (tr[0][i])
que.push(tr[0][i]);
while (!que.empty()){
ll u = que.front();
que.pop();
for (ll i = 0; i < 26; i++) {
if (tr[u][i]){
fail[tr[u][i]]=tr[fail[u]][i];
que.push(tr[u][i]);
}else{
tr[u][i]=tr[fail[u]][i];
}
}
}
for (ll i=1;i<=tot;i++)
insert(fail[i],i);
tot=-1;
dfs(0);
}
ll solve(){
f[0][0]=1;
for (ll i=1;i<=k;i++){
ll pre=query(tmp[i]);
for (ll j=1;j<=m;j++)
f[i][j]=(f[i-1][j-1]+f[i-1][j]*((j-pre>0)?(j-pre):0))%K;
add(tmp[i],1);
add(out[tmp[i]]+1,-1);
}
for (ll i=1;i<=k;i++){
add(tmp[i],-1);
add(out[tmp[i]]+1,1);
}
return f[k][m];
}
int main(){
cin>>n>>q;
for (ll i=1;i<=n;i++){
cin>>s[i];
inserts((char *)s[i].c_str(),i);
}
build();
for (ll i=1;i<=q;i++){
cin>>k>>m;
for (ll j=1;j<=k;j++){
cin>>a>>b;
tmp[j]=in[pos[a][b]];
}
sort(tmp+1,tmp+k+1);
cout<<solve()<<endl;
}
}
D - 第四章:不要停下来啊!!!!
数位DP
把(s)的所有后缀插入到AC自动机中
(f[i][j])表示前(i)位组成的串匹配到AC自动机的第(j)个节点
然后对答案的贡献和(B)题差不多,也要乘一下后缀积
最后上下界的答案做差即可
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll N=500+10,M=10,K=1e9+7;
ll q,d,tr[N*N][M],fail[N*N],dep[N*N],f[N*N],g[N*N],tot,pd;
char s[N],a[N],b[N];
queue<int> que;
void insert(char *s){
ll u=0;
for (ll i=0;s[i];i++) {
if (!tr[u][s[i]-'0'])
tr[u][s[i]-'0']=++tot;
u=tr[u][s[i]-'0'];
dep[u]=i+1;
}
}
void build(){
for (ll i=0;i<M;i++)
if (tr[0][i])
que.push(tr[0][i]);
while (!que.empty()){
ll u = que.front();
que.pop();
for (ll i=0;i<M;i++) {
if (tr[u][i]){
fail[tr[u][i]]=tr[fail[u]][i];
que.push(tr[u][i]);
}else{
tr[u][i]=tr[fail[u]][i];
}
}
}
}
ll solve(char *s,ll d){
pd=0;
ll ans=0;
memset(f,0,sizeof(f));
for (ll i=0;s[i];i++){
ans=(ans*10)%K;
memset(g,0,sizeof(g));
for (ll j=0;j<s[i]-'0';j++)
if (i!=0||j!=0){
if (dep[pd]!=d)
g[tr[pd][j]]=(g[tr[pd][j]]+1)%K;
else
ans=(ans+1)%K;
}
for (ll j=0;j<=tot;j++)
for (ll k=0;k<M;k++)
g[tr[j][k]]=(g[tr[j][k]]+f[j])%K;
for (ll j=0;j<=tot;j++)
if (dep[j]==d){
ans=(ans+g[j])%K;
g[j]=0;
}
memcpy(f,g,sizeof(f));
if (dep[pd]!=d) pd=tr[pd][s[i]-'0'];
}
return ans;
}
int main(){
scanf("%s",s);
for (ll i=0;s[i];i++) insert(s+i);
build();
scanf("%lld",&q);
for (ll i=1;i<=q;i++){
scanf("%s
%s
%lld",a,b,&d);
ll ans=solve(b,d);
ans=(ans+(dep[pd]==d))%K;
ans=(K+ans-solve(a,d))%K;
printf("%lld
",ans);
}
}
E - 第五章:与熊共斗
F - 第六章:最终决战
G - 一行超人
手写自动机
#include <iostream>
#include <map>
#include <vector>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
const ll K=1e9;
struct INS{
char op;
string a,b,c,d;
ll jmp;
}tmp;
map<string,ll> var;
vector<INS> ins;
vector<ll> jmp,in,out,ans;
void print(INS i){
printf("%c %s %s %s %s %lld
",i.op,
i.a.c_str(),i.b.c_str(),i.c.c_str(),i.d.c_str(),i.jmp);
}
void run(){
ll t=0,ip=0,inip=0;
while(ip!=ins.size()){
INS i=ins[ip];
if (t==100000&&i.op!='j'){printf("Time Limit Exceeded");return;}
if (i.op=='n'){
t++;
if (var.count(i.a)){printf("Runtime Error");return;}
var.insert(make_pair(i.a,0));
ip++;
}
if (i.op=='d'){
t++;
if (!var.count(i.a)){printf("Runtime Error");return;}
var.erase(i.a);
ip++;
}
if (i.op=='r'){
t++;
if (!var.count(i.a)){printf("Runtime Error");return;}
if (inip!=in.size()){
var[i.a]=in[inip];
inip++;
}
ip++;
}
if (i.op=='w'){
t++;
if (!var.count(i.a)){printf("Runtime Error");return;}
ans.push_back(var[i.a]);
ip++;
}
if (i.op=='='){
t++;
ll a,b,c;
if (!var.count(i.a)){printf("Runtime Error");return;}
if ('a'<=i.b[0]&&i.b[0]<='z'){
if (!var.count(i.b)){printf("Runtime Error");return;}
b=var[i.b];
}else{
b=atoi(i.b.c_str());
}
if ('a'<=i.c[0]&&i.c[0]<='z'){
if (!var.count(i.c)){printf("Runtime Error");return;}
c=var[i.c];
}else{
c=atoi(i.c.c_str());
}
if (i.d=="+") a=(b+c)%K;
if (i.d=="-") a=(b-c+K)%K;
if (i.d=="*") a=(b*c)%K;
if (i.d=="/"){
if (c==0){printf("Runtime Error");return;}
a=(b/c);
}
if (i.d=="%"){
if (c==0){printf("Runtime Error");return;}
a=(b%c);
}
var[i.a]=a;
ip++;
}
if (i.op=='i'||i.op=='l'){
t++;
ll a,b,c;
if ('a'<=i.a[0]&&i.a[0]<='z'){
if (!var.count(i.a)){printf("Runtime Error");return;}
a=var[i.a];
}else{
a=atoi(i.a.c_str());
}
if ('a'<=i.b[0]&&i.b[0]<='z'){
if (!var.count(i.b)){printf("Runtime Error");return;}
b=var[i.b];
}else{
b=atoi(i.b.c_str());
}
if (i.c=="<") c=(a<b);
if (i.c=="<=") c=(a<=b);
if (i.c==">") c=(a>b);
if (i.c==">=") c=(a>=b);
if (i.c=="==") c=(a==b);
if (i.c=="!=") c=(a!=b);
if (!c){
ip=i.jmp;
}else{
ip++;
}
}
if (i.op=='j'){
if (ins[i.jmp].op=='l'){
ip=i.jmp;
}else{
ip++;
}
}
}
ll len=out.size(),ac=0;
if (out.size()==ans.size()){
ac=1;
for (ll i=0;i<len;i++)
if (out[i]!=ans[i]) ac=0;
}
if (ac)
printf("Accepted");
else
printf("Wrong Answer");
}
int main(){
string s,t;
char ch=' ';
while(1){
tmp.a=tmp.b=tmp.c=tmp.d="";tmp.jmp=0;
while(ch==' '){ch=getchar();}s.clear();
if (ch=='
'){
ch=getchar();
if (!jmp.empty()) break;
while(1){
while(ch==' '){ch=getchar();}ll n=0;
if (ch=='
') break;
while('0'<=ch&&ch<='9'){n=n*10+ch-'0';ch=getchar();}
in.push_back(n);
}
ch=getchar();
while(1){
while(ch==' '){ch=getchar();}ll n=0;
if (ch=='
'||ch==EOF) break;
while('0'<=ch&&ch<='9'){n=n*10+ch-'0';ch=getchar();}
out.push_back(n);
}
run();
return 0;
}
if (ch=='}'){
ch=getchar();
if (jmp.empty()) break;
ins[jmp.back()].jmp=ins.size()+1;
tmp.op='j';
tmp.jmp=jmp.back();
ins.push_back(tmp);
jmp.pop_back();
continue;
}
while('a'<=ch&&ch<='z'){s=s+ch;ch=getchar();}
if (s=="new"||s=="delete"||s=="read"||s=="write"){
tmp.op=s[0];
while(ch==' '){ch=getchar();}t.clear();
while('a'<=ch&&ch<='z'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>10) break;
if (t=="new"||t=="delete"||t=="read"||t=="write"||t=="if"||t=="while") break;
while(ch==' '){ch=getchar();}if (ch!=';') break;ch=getchar();
tmp.a=t;
ins.push_back(tmp);
continue;
}
if (s=="if"||s=="while"){
tmp.op=s[0];
if (tmp.op=='w') tmp.op='l';
while(ch==' '){ch=getchar();}if (ch!='(') break;ch=getchar();
while(ch==' '){ch=getchar();}t.clear();
if ('a'<=ch&&ch<='z'){
t=t+ch;ch=getchar();
while('a'<=ch&&ch<='z'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>10) break;
if (t=="new"||t=="delete"||t=="read"||t=="write"||t=="if"||t=="while") break;
}else if ('0'<=ch&&ch<='9'){
t=t+ch;ch=getchar();
while('0'<=ch&&ch<='9'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>9||len>=2&&t[0]=='0') break;
}else break;
tmp.a=t;
while(ch==' '){ch=getchar();}t.clear();
while(ch=='<'||ch=='>'||ch=='!'||ch=='='){t=t+ch;ch=getchar();}
if (t!="<"&&t!="<="&&t!=">"&&t!=">="&&t!="=="&&t!="!=") break;
tmp.c=t;
while(ch==' '){ch=getchar();}t.clear();
if ('a'<=ch&&ch<='z'){
t=t+ch;ch=getchar();
while('a'<=ch&&ch<='z'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>10) break;
if (t=="new"||t=="delete"||t=="read"||t=="write"||t=="if"||t=="while") break;
}else if ('0'<=ch&&ch<='9'){
t=t+ch;ch=getchar();
while('0'<=ch&&ch<='9'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>9||len>=2&&t[0]=='0') break;
}else break;
tmp.b=t;
while(ch==' '){ch=getchar();}if (ch!=')') break;ch=getchar();
while(ch==' '){ch=getchar();}if (ch!='{') break;ch=getchar();
jmp.push_back(ins.size());
ins.push_back(tmp);
continue;
}
ll len=s.length();if (len<1||len>10) break;
while(ch==' '){ch=getchar();}if (ch!='=') break;ch=getchar();
while(ch==' '){ch=getchar();}t.clear();
if ('a'<=ch&&ch<='z'){
t=t+ch;ch=getchar();
while('a'<=ch&&ch<='z'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>10) break;
if (t=="new"||t=="delete"||t=="read"||t=="write"||t=="if"||t=="while") break;
}else if ('0'<=ch&&ch<='9'){
t=t+ch;ch=getchar();
while('0'<=ch&&ch<='9'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>9||len>=2&&t[0]=='0') break;
}else break;
tmp.op='=',tmp.a=s,tmp.b=t;
while(ch==' '){ch=getchar();}if (ch==';'){ch=getchar();
tmp.c="0",tmp.d="+";
ins.push_back(tmp);
continue;
}
if (ch!='+'&&ch!='-'&&ch!='*'&&ch!='/'&&ch!='%') break;tmp.d=ch;ch=getchar();
while(ch==' '){ch=getchar();}t.clear();
if ('a'<=ch&&ch<='z'){
t=t+ch;ch=getchar();
while('a'<=ch&&ch<='z'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>10) break;
if (t=="new"||t=="delete"||t=="read"||t=="write"||t=="if"||t=="while") break;
}else if ('0'<=ch&&ch<='9'){
t=t+ch;ch=getchar();
while('0'<=ch&&ch<='9'){t=t+ch;ch=getchar();}
ll len=t.length();if (len<1||len>9||len>=2&&t[0]=='0') break;
}else break;
tmp.c=t;
while(ch==' '){ch=getchar();}if (ch!=';') break;ch=getchar();
ins.push_back(tmp);
}
printf("Compile Error");
}
Q - 给小马染色
把原问题拆成两半(f1)和(f2),再合并起来
(f[s])表示黑马集合为(s)的方案数
然后对于(f1)中每个局面(s)只需要找出(f2)中与之不冲突的的所有局面(t)进行组合即可
然后这些局面(t)有包含关系,所以求(m)维的前缀和即可
注意这里要用(m)次一位前缀和来计算,不能用容斥原理计算
位移写成1<<a是错的,要改成1LL<<a才可以
#include <cstdio>
typedef long long ll;
const ll N=40;
ll n,m,p,q,a,b,d[N],f[(1<<20)],g[(1<<20)],ans,t;
int main(){
scanf("%lld%lld",&n,&m);
p=n/2,q=n-p;
for (ll i=1;i<=m;i++){
scanf("%lld%lld",&a,&b);
a--,b--;
d[a]|=1LL<<b,d[b]|=1LL<<a;
}
f[0]=1;
for (ll i=0;i<(1<<p);i++)
for (ll j=0;j<p;j++)
if (!(i&(1<<j))&&!(d[j]&i))
f[i|(1<<j)]|=f[i];
for (ll i=0;i<p;i++)
for (ll j=0;j<(1<<p);j++)
if (!(j&(1<<i)))
f[j|(1<<i)]+=f[j];
g[0]=1;
for (ll i=0;i<(1<<q);i++)
for (ll j=0;j<q;j++)
if (!(i&(1<<j))&&!((d[j+p]>>p)&i))
g[i|(1<<j)]|=g[i];
for (ll i=0;i<(1<<q);i++){
t=0;
for (ll j=0;j<q;j++)
if (i&(1<<j))
t|=d[j+p]&((1<<p)-1);
ans+=g[i]*f[(1<<p)-1-t];
}
printf("%lld",ans);
}
R - 小马的强迫症
搜索
选择一个区间肯定不能移到原来的位置
而且如果只移动一个元素(x)要么左面是(x-1)要么右面是(x+1)
把重复和无用的分治剪掉每步就只有84种可能
每一步最多使不连续的段减去3
所以再加个估价函数剪枝即可
#include <iostream>
#include <cstdio>
using namespace std;
int n,f[9][9],ans;
void dfs(int d){
int cnt=0;
for (int i=0;i<=n-2;i++)
if (f[d][i]>f[d][i+1])
cnt++;
if (cnt==0){
ans=min(d,ans);
return;
}
if (d+(cnt+2)/3>=ans)
return;
for (int i=0;i<=n-2;i++)
for (int j=i;j<=n-2;j++){
for (int k=j+1;k<=n-1;k++)
{
if (k==j+1&&(
f[d][k]==1&&i!=0||
f[d][k]!=1&&f[d][i-1]!=f[d][k]-1&&f[d][i]!=f[d][k]+1))
continue;
if (i==j&&(
f[d][i]==n&&k!=n-1||
f[d][i]!=n&&f[d][k]!=f[d][i]-1&&f[d][k+1]!=f[d][i]+1))
continue;
int pt=0;
for (int t=0;t<=i-1;t++) f[d+1][pt++]=f[d][t];
for (int t=j+1;t<=k;t++) f[d+1][pt++]=f[d][t];
for (int t=i;t<=j;t++) f[d+1][pt++]=f[d][t];
for (int t=k+1;t<=n-1;t++) f[d+1][pt++]=f[d][t];
dfs(d+1);
}
}
}
int main(){
scanf("%d",&n);
ans=n-1;
for (int i=0;i<n;i++) scanf("%d",&f[0][i]);
dfs(0);
printf("%d",ans);
}
S - 小马跑得快
和斗地主差不多
优先出不同牌的组合
最后贪心打完所有相同牌的组合
直接搜索即可
#include <cstdio>
#include <cstring>
#include <climits>
const int N=13;
int t,n,c[N+2],ans;
char s[]="A234567891JQK",name[3];
void dfs(int d);
int calc(){
int ans=0;
for (int i=0;i<N;i++) ans+=(3+c[i])/4;
return ans;
}
void multi(int d,int i,int len,int cnt){
int pt=i;
while(c[pt%N]>=cnt&&pt<=N) pt++;
if (pt-i>=len){
for (int j=i+len-1;j<pt;j++)
if (i!=j%N){
for (int k=i;k<=j;k++) c[k%N]-=cnt;
dfs(d+1);
for (int k=i;k<=j;k++) c[k%N]+=cnt;
}
}
}
void dfs(int d){
int one=calc();
if (d>=ans) return;
if (d+one<ans) ans=d+one;
if (!one) return;
for (int i=0;i<=N;i++){
multi(d,i,2,3);
multi(d,i,2,2);
multi(d,i,5,1);
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
ans=INT_MAX;
for (int i=0;i<=N;i++) c[i]=0;
for (int i=1;i<=n;i++){
scanf("%s",name);
c[strchr(s,name[0])-s]++;
}
dfs(0);
printf("%d
",ans);
}
}
T - 小马下棋
Alpha-Beta剪枝,经典博弈树
把每一状态的所有分支先按照收益排序
然后递归计算两个估价函数进行剪枝即可
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <climits>
using namespace std;
typedef pair<int,int> P;
typedef pair<int,P> P2;
const int N=5,K=8;
int n,k,m[N][N],ans[2],now;
int dfs(int d,int a,int b){
P2 tmp[N*N];
if (d==2*k) return now;
for (int i=0;i<n-1;i++)
for (int j=0;j<n-1;j++){
int t=m[i][j]+m[i+1][j]+m[i][j+1]+m[i+1][j+1];
tmp[i*(n-1)+j]=P2(t,P(i,j));
}
if (d%2==0)
sort(tmp,tmp+(n-1)*(n-1),greater<P2>());
else
sort(tmp,tmp+(n-1)*(n-1),less<P2>());
for (int k=0;k<(n-1)*(n-1);k++){
int i=tmp[k].second.first;
int j=tmp[k].second.second;
int t=m[i][j];
now+=m[i][j]+m[i+1][j]+m[i][j+1]+m[i+1][j+1];
m[i][j]=m[i][j+1],m[i][j+1]=m[i+1][j+1];
m[i+1][j+1]=m[i+1][j],m[i+1][j]=t;
if (d%2==0)
a=max(a,dfs(d+1,a,b));
else
b=min(b,dfs(d+1,a,b));
t=m[i][j];
m[i][j]=m[i+1][j],m[i+1][j]=m[i+1][j+1];
m[i+1][j+1]=m[i][j+1],m[i][j+1]=t;
now-=m[i][j]+m[i+1][j]+m[i][j+1]+m[i+1][j+1];
if (b<=a) break;
}
if (d%2==0)
return a;
else
return b;
}
int main(){
scanf("%d%d",&n,&k);
ans[0]=0,ans[1]=INT_MAX;
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
scanf("%d",&m[i][j]);
printf("%d",dfs(0,INT_MIN,INT_MAX));
}