NOI后记

这次是我第一次,也是最后一次NOI
由于我是夏令营选手,然后GD夏令营有5个人。每个宿舍能装4个人。5%4==1?
没错,我就是那个1
然后被果断发配到与3个黑龙江夏令营选手一起= =。

7.31
我们很晚才报道。然后与何神(我们尊称为小赖神)等吃了晚饭。。。
然后回到宿舍。发现没有拖鞋洗澡。疼,然后果断去买。
买完回来发现那个厕所激动得停不下来了,一直冲水,疼。。。
然后我们把他活生生拔出来以后才停了。过了一会儿,听过隔壁宿舍的厕所也开始激动了。于是心里平衡一点。。。
然后上上网,就睡了。

8.1
早上开幕式。。。然后各种讲话。让我感觉比较疼的是,映像中各个上去发表讲话的人里,只有1人脱稿。那就是海尔的经理。毕竟还是商人这方便要求比较高啊。。。
然后中午再围观了一下笔试题。下午就去笔试了。
笔试基本是送分。然后这次得了100。
然后那个练习赛的话,前面那题一看到题目搞了n^4的,可以AC了。然后提交答案没搞。。。出来一听,其他人都是O(n)的。。。我真蒟蒻+疼。
然后晚上很早就睡了。不过睡不惯那么硬的枕头。

8.2 Day1
比较紧张。然后看了第一题,哇。。。居然有80分是送人的。高高兴兴搞完,就不理第一题了。
然后看了第二题,感觉有点混乱。
写了个n^3的dp。然后看第3题。
大概思考了20秒。。。很神奇地认为是平面图最小割。但是那个两个不同方向边的不太会搞的样子啊。
然后我记得衡阳八中1001有人用sap依然可以卡过。。。甚至比我的spfa还快。于是我果断写了bfs预处理标号的sap。这样就能保证这题有80分了。
然后回来搞第二题。
第二题我一开始写了n^3的dp。后来用作对拍只用。感觉还是挺好的。
然后我YY了一会儿,想到了二分答案+平衡树的做法。然后就果断敲了。
后来写好以后。。。不会写对拍程序= =。。。我当时就泪奔了。。。
然后回忆笔试内容,知道运行一个文件时./xxxxx 然后我用C++的system("");试了一会儿。
搞的很囧。原因是我是这样写的:system("./makedata.exe");
然后。。。Linux的可执行文件原来是可以没后缀名的。。。
试了一会儿终于可以开始对拍了。
然后平衡树各种写庛= =。
调了N久终于正确了。然后好像我就没做什么了。就是各种对拍与检查文件名等。
然后下午拿到成绩。。。
80+30+90=200。
果断第二题zheyi了。。。70分的算法被我活生生写成30分。。。手测10^5那几个点都是2.xxx s。然后时限是2s。后来叉姐说不用struct封装那个平衡树,可以快3倍= =。。。我当时就囧了。

8.3 社会实践活动
上午去了蓬莱极地世界。。。然后围观各种鱼。然后就走了。囧。
下午去了那个啥沙滩。。。。看着海水泛着绿色的各种草还是啥的。。。果断留在沙滩上。。。
后来WY神牛在那各种打蜻蜓,但是一只都没搞到。表示蜻蜓敏捷太高。。。
后来省队众牛在各种讨论如何制止卢神牛进省队的伟大计划。。。叉姐猜测可能栋栋会因为没见过由卢神牛造成的大场面而泪流满面。。。
然后就回去洗洗睡了。

8.4 Day2
这次一拿到试题。先把所有题都看了一遍。
囧,一题都不会= =。然后貌似记得哪个大牛说要先搞提交答案题。然后我就先搞了。
然后我想了两个贪心的。一个是找距离最近的虾去抓。另一个是找追上时间最短的虾去抓。然后把所有数据都运行了一次,取较优值。
我表示没做过提交答案题。。。不知道要看数据。= =。
然后暴力的第一题和第二题。
然后第二题n=2的情况搞了好久。。。还是没搞出来。主要是顺指针逆时针走的和蛇形走的之间各种嵌套什么的。
然后想给第二题加一个连通性剪枝的。然后后来觉得反正搜索过不了多少,就没加。(事实证明,有30分)
第一题看了一下。决定分段。首先n<=10的30分一定要拿到。然后其他的就乱搞。
我先对那个先后限制当成权为1的有向边。然后Floyed了一下。
然后按照每个点卡的紧要程度拍了下序,然后搜索出一个可行解。那些最前可以是多少就放弃了。
全部输出1。

然后下午看成绩46+30+54=130。感觉第二题的30分比较zheyi。。。其他没什么了。
最后我也不知道什么rank的东西。感觉不知道有没有银牌。
然后后来听到说金牌貌似479,那我就觉得应该有银牌了。

8.5 之后
然后后面的就是各种找学校。
由于我比较囧。。。NOIP没有1=,又是夏令营选手。虽然NOI有430分。但是那个没有1=就没有保送资格。然后只能协商说等拿到一等以后再要。。。

然后现在离NOIP还有两个月。。。这两个月要好好学习文化课。。。毕竟NOIP虽然几乎不可能挂,但是毕竟没拿到保送。。。老师也不会允许我文化课不太努力的= =。。。。
现在。。。回去上文化课。现在就一个字:疼。。。。

[HDOJ2665 Kth number]【划分树】【例题】

【题目大意】给定一个数列。多个询问,问区间中第k小的数是哪个。

【算法分析】

既然是例题系列,那么讲一下划分树。

大体上是采用分治思想。每次取一中位数划分为左右两边。然后在同一区间内,数的顺序与在原数组中的顺序一样。

建立的话。我们可以通过自底向上的归并来达到lg n的建立。

然后询问时,通过To_L[]表示这个区间内,前i个数字有多少个被划分去了左边,可以快速判断。达到lg n的效果。

【其它】

这个英语太强大了。kth big number

【CODE】

#include

#include #include #include #include using namespace std;
const int N=105555;
int Tc,n,m,spt;
int a[N],b[N*20],To_L[N*20];
struct Node{int l,r,St,Ed;}Tr[N*5];
bool cmp(int i,int j){return a[i]     scanf("%d%d",&n,&m);
     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
     for (int i=1;i<=n;i++) b[i]=i;
     sort(b+1,b+n+1,cmp);
     spt=n;
}

void Union(int p,int s1,int e1,int s2,int e2){
     Tr[p].St=spt+1;
     int i=s1,j=s2,Sum=0;
     while (i<=e1 || j<=e2){
       if (i<=e1 && (j>e2 || b[i]                                    else b[++spt]=b[j++];
       To_L[spt]=Sum;
     }
     Tr[p].Ed=spt;
}

void build(int p,int l,int r){
     Tr[p].l=l; Tr[p].r=r;
     if (l==r){
       Tr[p].St=Tr[p].Ed=l;
       return;
     }
     int mid=(l+r)/2,i;
     build(p*2,l,mid);
     build(p*2+1,mid+1,r);
     Union(p,Tr[p*2].St,Tr[p*2].Ed,Tr[p*2+1].St,Tr[p*2+1].Ed);
}

int Query(int p,int l,int r,int k){
    if (Tr[p].l==Tr[p].r) return a[b[Tr[p].St]];
    int LL=0,RR,res;
    RR=To_L[Tr[p].St+r-1];
    if (l!=1) LL=To_L[Tr[p].St+l-2];
    res=RR-LL;
    if (res>=k) return Query(p*2, LL+1 , LL+res ,k);
    else{
      k-=res;
      res=r-l+1-res;
      if (l!=1) LL=l-1-LL;
      return Query(p*2+1,LL+1,LL+res,k);
    }
}

void solve(){
     int l,r,k,i;
     for (i=0;i         scanf("%d%d%d",&l,&r,&k);
         printf("%dn",Query(1,l,r,k));
     }
}

int main(){
    scanf("%d",&Tc);
    for (int i=0;i        init();
        build(1,1,n);
        solve();
    }
    return 0;
}

[HUST1407 山东省队选拔赛2008 郁闷的小J]【离线算法】【二分】【树状数组】★★

【题目连接】http://acm.hust.edu.cn/thx/problem.php?id=1407 (中文题目)

【算法分析】

这是一个好题。

首先有一个通用解法,平衡树+线段树,不过几乎肯定超时。(在线算法)

然后我YY到一个很经典的东西。P[j]-P[i-1]就得到i..j的信息。

于是对于一个询问,Q i j w。其实我们只要知道w在前j-1个位置有多少个,和w在前i个位置有多少个。就能通过一个减法得到答案。

然后我们把我们需要的信息抽出来。只维护他们。

每个信息的结构如下。{int Num,pos,key;}。其中Num表示询问的w。pos表示前pos个位置。key表示前pos个位置,Num出现几次?

然后我们观察假如change了一个数a[i]=p -> a[i]=w,该变的是哪些信息?

第一类,删除旧信息。对于所有Num=p,且pos>=i的信息,我们将他的key–

第二类,增加新信息。对于所有Num=w,且pos>=i的信息,我们将他的key++

好了。这里面我们需要的东西就显而易见了。

把信息按Num为第一关键字,pos为第二关键字排序,然后可以很容易用树状数组 or 线段树维护起来。

当然,我们当前位置+1。后面Num>w的是没有必要减回来的。因为我们最后是通过减法运算嘛。会互相抵消的。

【其它】

Orz。再删除了一次冗余的东西以后。排到了第一。

1 48386(2) edward_mj 4560 KB 1432 MS C++ 2382 B 2010-07-29 15:53:57 2 47267 lshmouse 6144 KB 1568 MS C++ 2727 B 2010-07-26 21:30:03 3 48114 foreverlin 5480 KB 1804 MS C++ 4771 B 2010-07-29 01:32:38 4 48102 WhatIsGenius 8548 KB 3128 MS C++ 1965 B 2010-07-28 23:09:53

【CODE】

#include #include #include #include #include using namespace std;
const int N=105555;
int n,m,tot;
int a[N];
struct Qu_t{int op,x,y,z;}Query[N];
struct List_t{int Num,pos;}L[N*2];
int Tr[N*2];

void init(){
     tot=0;
     scanf("%d%d",&n,&m);
     int i;
     for (i=1;i<=n;i++)
       scanf("%d",&a[i]);
     char tmp[10];
     for (i=1;i<=m;i++){
         scanf("%s",tmp);
         if (tmp[0]==’Q’){
           Query[i].op=0;
           scanf("%d%d%d",&Query[i].x,&Query[i].y,&Query[i].z);
           L[++tot].Num=Query[i].z; L[tot].pos=Query[i].y;
           L[++tot].Num=Query[i].z; L[tot].pos=Query[i].x-1;
         }
         else{
           Query[i].op=1;
           scanf("%d%d",&Query[i].x,&Query[i].y);
         }
     }
}

inline bool cmp(List_t A,List_t B){
     if (A.Num!=B.Num) return A.Num     return A.pos}

inline void Add(int k,int x){
     for (;k<=tot;k+=k&-k) Tr[k]+=x;
}

inline int Get_Sum(int k){
    int res=0;
    for (;k>0;k-=k&-k) res+=Tr[k];
    return res;
}

inline int Find(int key,int pos){
    int l=1,r=tot,mid;
    while (l      mid=(l+r)>>1;
      if (L[mid].Num                                                          else r=mid;
    }
    return l;
}

void pre_do(){
     sort(L+1,L+tot+1,cmp);
     int tmp=tot,i,x;
     tot=0;
     for (i=1;i<=tmp;i++){
         tot+=(i==1 || L[i].Num!=L[i-1].Num || L[i].pos!=L[i-1].pos);
         L[tot]=L[i];
     }
     for (i=1;i<=tot;i++) Tr[i]=0;
     for (i=1;i<=n;i++){
         x=Find(a[i],i);
         if (L[x].Num==a[i] && L[x].pos>=i) Add(x,1);
     }
}

void solve(){
     int i,j,x,y;
     for (i=1;i<=m;i++)
       if (Query[i].op==0){
         x=Get_Sum( Find ( Query[i].z,Query[i].y ) );
         y=Get_Sum( Find ( Query[i].z,Query[i].x-1 ) );
         printf("%dn",x-y);
       }
       else{
         x=Find(a[Query[i].x],Query[i].x);
         if (L[x].Num==a[Query[i].x] && L[x].pos>=Query[i].x) Add(x,-1);
        
         a[Query[i].x]=Query[i].y;
        
         x=Find(a[Query[i].x],Query[i].x);
         if (L[x].Num==a[Query[i].x] && L[x].pos>=Query[i].x) Add(x,1);
       }
}

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

[FZU1540 迎接光棍节]枚举+KMP Or 二分+后缀数组

【题目】多串的最长等差连续子串。中文题目:http://acm.fzu.edu.cn/problem.php?pid=1540

【算法分析】

这个作为后缀数组的经典例题之一。。。就不用说了。。。

但是很囧的是,我写的后缀数组MLE了= =。由于我的后缀数组特别搓,所以套模板的人们应该都可以过。

注意到这题的特殊性,n<=4000,每个串长<=200。

所以可以有一个暴力解法。

先等差处理。(a[i]-a[i-1])

然后枚举答案在串1中开始的位置。

于是以第一个串从枚举的开始位置到第一个串末端作为模式串。

去和n-1个串暴力KMP匹配。可以得到最多能配多长。

然后就能过了。

【时间复杂度】O(Len*Sum) Len为第一个串的长度。Sum为所有串的字母数之和。

【空间复杂度】O(Sum)

【CODE】

#include

[SGU176 Flow construction]【最小流】

【题目大意】
给定源汇,一些弧必须满载。问源到汇的最小流是多少?
【算法分析】
通过加弧当然。。。这是最简单的方法。。。反向增广这么高科技的就算了。。。
【其它】
复习上下界网络流。居然没1Y。原因1是有一个地方N写成E了。(就是数组没开够。。。),原因2是MLE。
我的习惯一直是把数组开大很多的。。。SGU这种限这么死的地方就果断悲剧。
【CODE】
#include #include #include const int N=155;
const int E=N*N*6;
struct edge{int x,y,c,next;};

struct Network_t{
    int e,S,T,Flow;
    edge g[E];
    int ls[N],fa[N],cur[N],num[N],d[N];
    void init(){
        e=1;
        memset(ls,0,sizeof(ls));
    }

    void clr(){
        for (int i=3;i<=e;i+=2){
            g[i-1].c+=g[i].c;
            g[i].c=0;
        }
    }

    void addedge(int x,int y,int c){
        g[++e].x=x; g[e].y=y; g[e].c=c; g[e].next=ls[x]; ls[x]=e;
        g[++e].x=y; g[e].y=x; g[e].c=0; g[e].next=ls[y]; ls[y]=e;
    }

    void relabel(int k){
        cur[k]=ls[k];
        int mm=T;
        for (int t=ls[k];t;t=g[t].next)
          if (g[t].c && d[g[t].y]            mm=d[g[t].y];
        d[k]=mm+1;
    }

    void change(){
        int nf=0x7FFFFFFF;
        for (int i=T;i!=S;i=g[fa[i]].x)
          if (g[fa[i]].c        for (int i=T;i!=S;i=g[fa[i]].x){
            g[fa[i]].c-=nf;
            g[fa[i]^1].c+=nf;
        }
        Flow+=nf;
    }

    void sap(){
        Flow=0;
        int i;
        for (i=1;i<=T;i++){
            d[i]=num[i]=0;
            cur[i]=ls[i];
        }
        i=S;
        while (d[S]            int &t=cur[i];
            for (;t;t=g[t].next)
              if (g[t].c && d[g[t].y]+1==d[g[t].x]) break;
            if (t){
                fa[g[t].y]=t;
                i=g[t].y;
                if (i==T){
                    change();
                    i=S;
                }
            }
            else{
                if (!–num[d[i]]) break;
                relabel(i);
                ++num[d[i]];
                if (i!=S) i=g[fa[i]].x;
            }
        }
    }
}Network;

int n,m,SF;
int din[N],dout[N],Type[E],C[E];

void init(){
    Network.init();
    scanf("%d%d",&n,&m);
    SF=0;
    for (int x,y,c,t,i=1;i<=m;i++){
      scanf("%d%d%d%d",&x,&y,&c,&t);
      if (t){
          din[y]+=c;
          dout[x]+=c;
          SF+=c;
      }
      else Network.addedge(x,y,c);
      Type[i]=t;
      C[i]=c;
    }
}

void build(){
    Network.S=n+1; Network.T=n+2;
    int i;
    for (i=1;i<=n;i++){
        if (din[i]) Network.addedge(n+1,i,din[i]);
        if (dout[i]) Network.addedge(i,n+2,dout[i]);
    }
    Network.addedge(n,1,0x7FFFFFFF);
}

void output(){
    int cnt=1;
    for (int i=1;i<=m;i++){
      if (Type[i]) printf("%d",C[i]);
      else printf("%d",Network.g[cnt+=2].c);
      if (i==m) puts("");
           else putchar(‘ ’);
    }
}

void solve(){
    Network.sap();
    if (Network.Flow!=SF){
        puts("Impossible");
        return;
    }
    int l,r,mid;
    l=0; r=Network.g[Network.e].c;
    while (l        mid=(l+r)/2;
        Network.clr();
        Network.g[Network.e-1].c=mid;
        Network.sap();
        if (Network.Flow==SF) r=mid;
                         else l=mid+1;
    }
    Network.clr();
    Network.g[Network.e-1].c=l;
    Network.sap();
    printf("%dn",l);
    output();
}

int main(){
    init();
    build();
    solve();
    return 0;
}