第一次打usaco,感觉题目还挺有趣的。(为啥感觉美国人的英语比日、俄的英语还要难读呢)
Standinfg Out from the Herd.
题意:给n个字符串,对每个字符串求有多少本质不同的子串只在这个字符串中出现。
这题我直接求了一个广义后缀自动机,然后给每个点一个标记表示这个点对应的子串是否只在某个字符串中出现。然后随便统计一下就做完了。O(N)。
AC代码如下:
#include<bits/stdc++.h>
#define N 200009
using namespace std;
int n; long long ans[N]; char s[N];
struct sam{
int tot,fst[N],nxt[N],ch[N][26],fa[N],len[N],f[N];
sam(){ tot=1; }
void calc(int x,int y){ f[x]=(f[x] && f[x]!=y?-1:y); }
int add(int k,int c,int p){
int np=++tot; calc(np,k); len[np]=len[p]+1;
for (; p && !ch[p][c]; p=fa[p]) ch[p][c]=np;
if (!p){
fa[np]=1; return np;
}
int q=ch[p][c];
//cerr<<" "<<k<<' '<<c<<' '<<p<<' '<<q<<' '<<len[p]<<' '<<len[q]<<endl;
if (len[q]==len[p]+1) fa[np]=q; else{
int nq=++tot; len[nq]=len[p]+1;
//cerr<<nq<<endl;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
//cerr<<q<<' '<<np<<endl;
for (; p && ch[p][c]==q; p=fa[p]) ch[p][c]=nq;
}
return np;
}
void dfs(int x){
int y;
for (y=fst[x]; y; y=nxt[y]) dfs(y);
if (f[x] && f[x]!=-1) ans[f[x]]+=len[x]-len[fa[x]];
calc(fa[x],f[x]);
}
void solve(){
int i;
for (i=2; i<=tot; i++){
nxt[i]=fst[fa[i]]; fst[fa[i]]=i;
}
dfs(1);
}
}sam;
int main(){
freopen("standingout.in","r",stdin); freopen("standingout.out","w",stdout);
scanf("%d",&n);
int i,j,len,last;
for (i=1; i<=n; i++){
scanf("%s",s+1);
len=strlen(s+1);
for (j=1,last=1; j<=len; j++) last=sam.add(i,s[j]-'a',last);
}
sam.solve();
for (i=1; i<=n; i++) printf("%lld\n",ans[i]);
return 0;
}
题意:有障碍的地图上一个人推一个箱子,多次询问箱子最后能否到达目的地。
显然要预处理全部的答案,然后每次O(1)询问。考虑用f[i][j][k]表示箱子在i,j,人在箱子的k方向上能否到达。有两种转移,分别是推一步箱子和改变人的位置。能够改变人的位置当且仅当两个点在同一个点双。
AC代码如下:
#include<bits/stdc++.h>
#define N 2009
#define M 2500009
#define E 10000009
using namespace std;
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
int m,n,cas,dfsclk,tot=1,cnt,tp,pt,p[N][N],px[M],py[M];
int fst[M],pnt[E],nxt[E],dfn[M],low[M],q[M],fa[M<<1];
char s[N][N]; bool ans[N][N][4],vis[N][N];
struct node{ int x,y,k; }h[M];
void add(int x,int y){
if (!x || !y) return;
pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x,int p){
int i,y; dfn[x]=low[x]=++dfsclk; q[++tp]=x;
for (i=fst[x]; i; i=nxt[i]) if (i!=(p^1)){
y=pnt[i];
if (!dfn[y]){
dfs(y,i); low[x]=min(low[x],low[y]);
if (low[y]==dfn[x]){
for (pt++; q[tp]!=y; tp--){
fa[q[tp]]=pt;
}
fa[q[tp--]]=pt; fa[pt]=x;
} else if (low[y]>dfn[x]){
while (q[tp]!=y) tp--; tp--;
}
} else low[x]=min(low[x],dfn[y]);
}
}
bool check(int x,int y){
if (!x || !y) return 0;
//cerr<<"check: "<<x<<' '<<y<<endl;
return fa[x] && fa[x]==fa[y] || fa[fa[x]]==y || fa[fa[y]]==x;
}
void bfs(int sx,int sy){
int head=0,tail=1,i; node u,v;
h[1]=(node){sx,sy,0}; vis[sx][sy]=1;
while (head<tail){
u=h[++head];
for (i=0; i<4; i++){
v=(node){u.x+dx[i],u.y+dy[i],0};
if (s[v.x][v.y]!='B' && !vis[v.x][v.y] && p[v.x][v.y]){
vis[v.x][v.y]=1; h[++tail]=v;
}
}
}
}
int main(){
freopen("pushabox.in","r",stdin); freopen("pushabox.out","w",stdout);
scanf("%d%d%d",&m,&n,&cas);
int i,j,k,x,y;
for (i=1; i<=m; i++){
scanf("%s",s[i]+1);
for (j=1; j<=n; j++) if (s[i][j]!='#'){
p[i][j]=++cnt;
px[cnt]=i; py[cnt]=j;
}
}
for (i=1; i<=cnt; i++)
for (j=2; j<4; j++){
add(i,p[px[i]+dx[j]][py[i]+dy[j]]);
add(p[px[i]+dx[j]][py[i]+dy[j]],i);
}
pt=cnt;
for (i=1; i<=cnt; i++) if (!dfn[i]) dfs(i,0);
int Ax,Bx,Ay,By;
for (i=1; i<=m; i++)
for (j=1; j<=n; j++) if (s[i][j]=='A'){
Ax=i; Ay=j;
} else if (s[i][j]=='B'){
Bx=i; By=j;
}
bfs(Ax,Ay);
int head=0,tail=0;
for (i=0; i<4; i++)
if (vis[Bx+dx[i]][By+dy[i]]){
h[++tail]=(node){Bx,By,i};
ans[Bx][By][i]=1;
// cerr<<dx[i]<<' '<<dy[i]<<endl;
}
node u,v;
while (head<tail){
u=h[++head];
// cerr<<u.x<<' '<<u.y<<' '<<i<<endl;
k=(u.k+2)&3; v=(node){u.x+dx[k],u.y+dy[k],u.k};
if (p[v.x][v.y] && !ans[v.x][v.y][v.k]){
ans[v.x][v.y][v.k]=1; h[++tail]=v;
}
k=p[u.x+dx[u.k]][u.y+dy[u.k]];
// cerr<<u.x+dx[u.k]<<' '<<u.y+dy[u.k]<<endl;
for (i=0; i<4; i++)
if (check(p[u.x+dx[i]][u.y+dy[i]],k))
if (!ans[u.x][u.y][i]){
//cerr<<i<<' '<<u.x+dx[i]<<' '<<u.y+dy[i]<<' '<<"gg"<<endl;
ans[u.x][u.y][i]=1; h[++tail]=(node){u.x,u.y,i};
}
}
while (cas--){
scanf("%d%d",&x,&y);
puts(x==Bx && y==By || ans[x][y][0] || ans[x][y][1] || ans[x][y][2] || ans[x][y][3]?"YES":"NO");
}
return 0;
}
题意:有n头奶牛,每头奶牛有一个数值a[i],表示当这个奶牛在首位的时候,它会跑到倒数第a[i]+1位上。问有多少头奶牛永远不能到首位。
首先令a[i]=n-a[i]。考虑如果a[1]=1,那么显然答案为n-1。进一步发现如果存在一个大小为M的集合{x1,...,xM},满足x1<...<xM且a[x1],...,a[xM]<=M,那么xM之后的一定不能到达首位。因此考虑二分答案,然后判断前缀中是否存在这样一个集合。如果当前不合法,就删去最大的一个,重复上述操作后如果还有点剩下来,那么剩下来的就是一个合法的集合。
AC代码如下:
#include<bits/stdc++.h>
#define N 100009
using namespace std;
int n,m,a[N];
priority_queue<int> Q;
bool check(int x){
int i;
while (!Q.empty()) Q.pop();
for (i=1; i<=x; i++) Q.push(n-a[i]);
for (; !Q.empty() && Q.top()>x; Q.pop()) x--;
return x>0;
}
int main(){
freopen("greedy.in","r",stdin); freopen("greedy.out","w",stdout);
scanf("%d",&n);
int i;
for (i=1; i<=n; i++) scanf("%d",&a[i]);
int l=1,r=n,mid;
while (l<r){
mid=l+r>>1;
if (check(mid)) r=mid; else l=mid+1;
}
printf("%d\n",n-l);
return 0;
}
by lych
2017.12.23