ceoi2011選做


       就做了王宏的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;
}

   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章