就做了王宏的ppt上提到的3道题(好吧,其实提到了4道题,但是treasure hunt想法太烦了=。=,还不能测,就算了吧=。=!)
题意就不赘述了。
matching:
这道题主要问题是如何修改kmp的匹配方式使得kmp可以在o(logn)或o(1)的时间判断是否匹配,而且可以使用next数组。
对于模式串预处理, 如果对于模式串的第i个位置,找出在它前面且刚好比它大的数的位置,以及在它前面且刚好比它小的数的位置,如果对于文本串的j,j往前推相对应的位置上的数也满足相同的大小关系,那么匹配成功,否则失败,这样就可以在o(1)的时间得到是否匹配。
# include <cstdlib> # include <cstdio> # include <cmath> # include <cstring> using namespace std; const int maxn = 1000000+20; int bite[maxn], s[maxn], h[maxn], next[maxn], prev[maxn], c[maxn], big[maxn], sml[maxn], p[maxn], ans[maxn]; int n, m; void prepare() { int i; for (i = 1; i <= n; i++) bite[s[i]]= i, next[i]=i+1, prev[i]=i-1; for (i = n; i >= 1; i--) { if (next[s[i]] != n+1) big[i] = i-bite[next[s[i]]]; else big[i]=-1; if (prev[s[i]] != 0) sml[i] = i-bite[prev[s[i]]]; else sml[i]=-1; prev[next[s[i]]]=prev[s[i]]; next[prev[s[i]]] = next[s[i]]; } } inline bool compare(int c[maxn], int i, int j) { if (big[j]!= -1&& c[i-big[j]] < c[i]) return false; if (sml[j]!= -1&& c[i-sml[j]] > c[i]) return false; return true; } void kmp() { int i,j; for (i = 2, j = 0, p[1]= 0; i <= n; i++) { for (;j > 0 && (!compare(s, i, j+1)); j=p[j]); if (compare(s,i,j+1)) j++; p[i]= j; } for (i= 1, j =0; i <= m; i++) { for (;j > 0 && (!compare(h, i, j+1)); j=p[j]); if (compare(h,i,j+1)) j++; if (j == n) ans[0]++, ans[ans[0]]= i-n+1,j= p[j]; } printf("%d\n", ans[0]); for (i = 1; i <= ans[0]; i++) printf("%d ", ans[i]); } int main() { int i; //freopen("match.in", "r", stdin); //freopen("match.out", "w", stdout); scanf("%d%d", &n, &m); int d; for (i = 1; i <= n; i++) {scanf("%d", &d); s[d]=i;}; for (i = 1; i <= m; i++) scanf("%d", &h[i]); prepare(); kmp(); return 0; }
team:
一开始以为是个傻X贪心,结果在ppt上找到了反例,其实贪心的错误之处在于,贪心认为剩下的人越多,那么分出的组就越多,这是错误的。
然后想到了一个傻X的o(n^2) dp, 优化势在必行。 稍微看一下之后发现直接用线段树傻X维护就可0~i-a[ i ]中的最优值就可以了。
但是ppt上貌似完全没提这种方法?=。=!当然居然有o(n)的算法,是在木有看懂,ym~~orz
# include <cstdlib> # include <cstdio> # include <cstring> using namespace std; const int maxn=1000000+5, oo=1073741819; int a[maxn], id[maxn],te[maxn*4], next[maxn*3], sum[maxn*3], link[maxn*3], pre[maxn], f[maxn]; int n, top, st, m; inline void linke(int x, int y) { ++top; next[top]=link[x]; link[x]=top; sum[top]=y; } inline int max(int x, int y) { if (f[y]>f[x] ||(f[y]==f[x]&&y>x)) return y; else return x; } void query(int x, int d) { int now; for (te[now=st+x]=d,now>>=1; now>0; now>>=1) te[now]=max(te[now<<1], te[(now<<1)+1]); } int ask(int l, int r) { int ans=n+1; l=l+st-1;r=r+st+1; for (;(l^r)!= 1;l>>=1,r>>=1) { if (!(l&1)) ans=max(ans,te[l+1]); if ( r&1) ans=max(ans,te[r-1]); } return ans; } void prepare() { int i; for (st= 1; st <= n;st <<= 1); st++; for (i = 1; i<=st*2; i++) te[i]=n+1; query(0, 0); } int main() { int ke,i,j,d; //freopen("team.in", "r", stdin); // freopen("team.out", "w", stdout); scanf("%d", &n); for (i = 1; i <= n; i++) { scanf("%d", &d); linke(d, i); } for (i = 1; i <= n; i++) for (ke= link[i]; ke!= 0; ke=next[ke]) a[++m]=i, id[m]= sum[ke]; n=m; f[0] = 0; f[n+1]=-oo; prepare(); for (i = 1; i <= n; i++) { if (a[i]>i) f[i]=-oo; else pre[i]=ask(0, i-a[i]), f[i]=f[pre[i]]+1; query(i, i); } printf("%d\n", f[n]); /* for (i=n; i >=1;) { j= pre[i]; printf("%d ", i-j); for (;i>j;i--) printf("%d ", id[i]); printf("\n"); }*/ return 0; }
traffic:
一开始以为tarjan缩点然后记忆化裸搜就可以了(后来貌似还是这样),但是写了之后才发现自己的dp是会重复计算的,样例都过不了=。=!以后写题目除非特殊情况,务必手算样例。
然后经历了漫长的修改过程,借鉴引水入域的思想,用平面图的性质得到,一个“东”点可以到达的“西”点必定是连续的一段(永远不能到达的“西”点除外)。
于是弧,写的越来越丑,一次tarjan一次dfs,两次重构图,没办法各种数组重复利用=。=!,居然写了4kb(neroysq就写了不到60行=。=),最后小数据没错大数据一片wa(果然是非文艺程序员),后来自己构了个超水的16格点的网格图拍,居然还拍出来了=。=!,结果繁杂的地方没错,反而是tarjan写错了(羞愧ing,tarjan写错概率很高)。
然后无限欣喜的过了poi网站上的数据,结果在bzoj上跑了10s给了一个RE, TAT,时运不齐,命运多舛了,实在调不出,不知道bzoj上有什么奇葩数据>.<;
所以这个代码嘛......
# include <cstdlib> # include <cstdio> # include <cmath> # include <cstring> using namespace std; const int maxn=300000+10, maxm=900000+10, oo=1073741819; int top,top2,link2[maxm*3], next2[maxm*3], sum2[maxm*3],linke[maxm*3], next[maxm*3], sum[maxm*3]; int low[maxn], dfn[maxn], w[maxn],st[maxn]; bool step[maxn], stay[maxn]; int id[maxn], place[maxn], pt[maxn], pe[maxn], pb[maxn],l[maxn], r[maxn]; int n,m,A,B,tot,time; void link(int x, int y) {++top; next[top]=linke[x]; linke[x]=top; sum[top]=y;}; inline int min(int x, int y) {return x<y?x:y;}; inline int max(int x, int y) {return x>y?x:y;}; void tarjan(int p) { int ke=linke[p],now;low[p]=dfn[p]=++time;st[++tot]=p; step[p]=true; stay[p]=true; for (;ke!= 0; ke=next[ke]) if (!step[sum[ke]]) tarjan(sum[ke]), low[p]=min(low[p], low[sum[ke]]); else if (stay[sum[ke]]) low[p]=min(low[p], dfn[sum[ke]]); if (dfn[p]==low[p]) { for (now=st[tot];now!=p;now=st[--tot]) id[now]=p, w[p]+= w[now], stay[now]=false, l[p]=min(l[p],l[now]), r[p]=max(r[p],r[now]); tot--; stay[now]=false; } } void dfs(int p) { int ke=link2[p]; step[p]=true; for (;ke!=0; ke=next2[ke]) { if (!step[sum2[ke]]) dfs(sum2[ke]), step[sum2[ke]]=true; r[p]=max(r[p], r[sum2[ke]]); l[p]=min(l[p],l[sum2[ke]]); } } void sort(int pt[maxn], int l, int r) { int i=l, j=r, tmp,d=place[pt[l+r>>1]]; for (;i <= j;) { for (;place[pt[i]] < d;i++); for (;place[pt[j]] > d;j--); if (i <= j) tmp=pt[i],pt[i]=pt[j],pt[j]=tmp,i++,j--; } if (i<r) sort(pt,i,r); if (l<j) sort(pt,l,j); } void linke2(int x, int y) { ++top2; next2[top2]=link2[x];link2[x]=top2;sum2[top2]=y; }; void dfs2(int p) { step[p]= true; for (int ke=link2[p]; ke!= 0; ke=next2[ke]) if (!step[sum2[ke]])dfs2(sum2[ke]); } void prepare() { int i,x,y,z; sort(pt,1,pt[0]); sort(pb,1,pb[0]); for (i = 1; i <= m; i++) { scanf("%d%d%d",&x, &y, &z); linke2(x,y); if (z==2) linke2(y,x); } for (i = 1; i <= pt[0]; i++) dfs2(pt[i]); for (i = 1; i <= n; i++) for (int ke=link2[i]; ke!= 0; ke=next2[ke]) if (step[i] && step[sum2[ke]]) link(i, sum2[ke]); for (i = 1; i <=pb[0]; i++) if (step[pb[i]]) pe[++pe[0]] = pb[i], l[pb[i]]=r[pb[i]]=pe[0]; memset(next2, 0, sizeof(next2)); memset(sum2, 0, sizeof(sum2)); memset(link2, 0, sizeof(link2)); memset(step, 0, sizeof(step)); top2=0; } int main() { int i,x,y; //freopen("traffic.in", "r", stdin); //freopen("traffic.out", "w", stdout); scanf("%d%d%d%d", &n, &m, &A, &B); for (i = 1; i <= n; i++) { scanf("%d%d", &x, &y); place[i]=y; l[i]=oo; r[i]=-oo; if (x==0) pt[++pt[0]]=i; else if (x==A) pb[++pb[0]]=i; } prepare(); memset(step, false, sizeof(step)); for (i =1; i <= n; i++) id[i]=i; for (i = 1; i <= n; i++) if (!step[i]) tarjan(i); memset(step, false, sizeof(step)); for (i = 1; i <= n; i++) for (int ke=linke[i]; ke!= 0; ke=next[ke]) if (id[i]!= id[sum[ke]]) linke2(id[i], id[sum[ke]]); for (i = 1; i <= n; i++) if (!step[i]) dfs(i); for (i = pt[0]; i >= 1; i--) if (r[id[pt[i]]] != -oo) printf("%d\n", r[id[pt[i]]]-l[id[pt[i]]]+1); else printf("0\n"); return 0; }