SQYBI.com

Always Challenge Miracle

Archive for the ‘OI’ tag

NOIP 2008 第四题 双栈排序(twostack) 题解

with 25 comments

这可能是第一份正确的完整题解.

这个帖子: http://www.oibh.org/bbs/thread-26983-1-2.html 是此算法的来源,但是帖子中说的不是很清楚,而且没有证明.题解中将把这些问题解决.
十分感谢inzaghi250(sqybi注:还好我不是inzaghi的fans…)提供的算法.

这道题大概可以归结为如下题意:
有两个队列和两个栈,分别命名为队列1(q1),队列2(q2),栈1(s1)和栈2(s2).最初的时候,q2,s1和s2都为空,而q1中有n个数(n<=1000),为1~n的某个排列.
现在支持如下四种操作:
a操作,将 q1的首元素提取出并加入s1的栈顶.
b操作,将s1的栈顶元素弹出并加入q1q2的队列尾.
c操作,将 q1的首元素提取出并加入s2的栈顶.
d操作,将s2的栈顶元素弹出并加入q1q2的队列尾.
请判断,是否可以经过一系列操作之后,使得q2中依次存储着1,2,3,…,n.如果可以,求出字典序最小的一个操作序列.

这道题的错误做法很多,错误做法却能得满分的也很多,这里就不多说了.直接切入正题,就是即将介绍的这个基于二分图的算法.
注意到并没有说基于二分图匹配,因为这个算法和二分图匹配无关.这个算法只是用到了给一个图着色成二分图.

第一步需要解决的问题是,判断是否有解.

考虑对于任意两个数q1[i]和q1[j]来说,它们不能压入同一个栈中的充要条件是什么(注意没有必要使它们同时存在于同一个栈中,只是压入了同一个栈).实际上,这个条件p是:存在一个k,使得i<j<k且q1[k]<q1[i]<q1[j].

首先证明充分性,即如果满足条件p,那么这两个数一定不能压入同一个栈.这个结论很显然,使用反证法可证.
假设这两个数压入了同一个栈,那么在压入q1[k]的时候栈内情况如下:
…q1[i]…q1[j]…
因为q1[k]比q1[i]和q1[j]都小,所以很显然,当q1[k]没有被弹出的时候,另外两个数也都不能被弹出(否则q2中的数字顺序就不是1,2,3,…,n了).
而之后,无论其它的数字在什么时候被弹出,q1[j]总是会在q1[i]之前弹出.而q1[j]>q1[i],这显然是不正确的.

接下来证明必要性.也就是,如果两个数不可以压入同一个栈,那么它们一定满足条件p.这里我们来证明它的逆否命题,也就是”如果不满足条件p,那么这两个数一定可以压入同一个栈.”
不满足条件p有两种情况:一种是对于任意i<j<k且q1[i]<q1[j],q1[k]>q1[i];另一种是对于任意i<j,q1[i]>q1[j].
第一种情况下,很显然,在q1[k]被压入栈的时候,q1[i]已经被弹出栈.那么,q1[k]不会对q1[j]产生任何影响(这里可能有点乱,因为看起来,当q1[j]<q1[k]的时候,是会有影响的,但实际上,这还需要另一个数r,满足j<k<r且q1[r]<q1[j]<q1[k],也就是证明充分性的时候所说的情况…而事实上我们现在并不考虑这个r,所以说q1[k]对q1[j]没有影响).
第二种情况下,我们可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈.
这样,原命题的逆否命题得证,所以原命题得证.

此时,条件p为q1[i]和q1[j]不能压入同一个栈的充要条件也得证.

这样,我们对所有的数对(i,j)满足1<=i<j<=n,检查是否存在i<j<k满足p1[k]<p1[i]<p1[j].如果存在,那么在点i和点j之间连一条无向边,表示p1[i]和p1[j]不能压入同一个栈.此时想到了什么?那就是二分图~
二分图的两部分看作两个栈,因为二分图的同一部分内不会出现任何连边,也就相当于不能压入同一个栈的所有结点都分到了两个栈中.
此时我们只考虑检查是否有解,所以只要O(n)检查出这个图是不是二分图,就可以得知是否有解.

此时,检查有解的问题已经解决.接下来的问题是,如何找到字典序最小的解.
实际上,可以发现,如果把二分图染成1和2两种颜色,那么结点染色为1对应当前结点被压入s1,为2对应被压入s2.为了字典序尽量小,我们希望让编号小的结点优先压入s1.
又发现二分图的不同连通分量之间的染色是互不影响的,所以可以每次选取一个未染色的编号最小的结点,将它染色为1并从它开始DFS染色,直到所有结点都被染色为止.这样,我们就得到了每个结点应该压入哪个栈中.接下来要做的,只不过是模拟之后输出序列啦~

还有一点小问题,就是如果对于数对(i,j),都去枚举检查是否存在k使得p1[k]<p1[i]<p1[j]的话,那么复杂度就升到了O(n^3).解决方法就是,首先预处理出数组b,b[i]表示从p1[i]到p1[n]中的最小值.接下来,只需要枚举所有数对(i,j),检查b[j+1]是否小于p1[i]且p1[i]是否小于p1[j]就可以了.

附代码(除去注释不到100行),带注释.代码中的a数组对应文中的队列p1.
已经过掉所有标准数据,以及5 7 2 4 1 6 3这组让很多贪心程序挂掉的数据~

xpycc  November 29th, 2008 at 09:24

这位大牛不妨试试以下数据:
1000
2 3 4 5 … 1000 1
对于这组数据 MS 您的程序崩溃了……

原因是这组数据中无向边的条数为 999*999 ,而您的内存只申请了 1002*2 。

sqybi  November 29th, 2008 at 11:29

@xpycc
嗯…程序貌似是有bug的…
而且判无解的地方貌似也有问题…

#include

using namespace std;

const int nn = 1002, mm = nn * 2, inf = 1000000000;
int n, tot, now;
int a[nn], b[nn], head[nn], color[nn];
int adj[mm], next[mm];
int stack[3][nn];
bool result;

void addEdge(int x, int y) //加边
{
    ++ tot;
    adj[tot] = y;
    next[tot] = head[x];
    head[x] = tot;
}

bool dfs(int i) //DFS染色,检查图是否是二分图的经典算法
{
    int temp = head[i];
    while (temp) //邻接表,检查每一条边
    {
        if (! color[adj[temp]]) //如果与当前结点的结点还未染色
        {
            color[adj[temp]] = 3 - color[i]; //进行染色
            dfs(adj[temp]); //DFS
        }
        if (color[adj[temp]] == color[i]) return false;
            //如果两个相邻结点染色相同,说明此图不是二分图,返回无解
        temp = next[temp];
    }
    return true;
}

int main()
{
    freopen("twostack.in", "r", stdin);
    freopen("twostack.out", "w", stdout);

    //输入
    scanf("%d", &n);
    for (int i = 1; i <= n; ++ i) scanf("%d", &a[i]);

    //预处理b数组
    b[n + 1] = inf;
    for (int i = n; i >= 1; -- i) b[i] = min(b[i + 1], a[i]); //"min" in STL

    //枚举数对(i,j)并加边
    tot = 0;
    for (int i = 1; i <= n; ++ i)
        for (int j = i + 1; j <= n; ++ j)
            if (b[j + 1] < a[i] && a[i] < a[j])
            {
                addEdge(i, j);
                addEdge(j, i);
            }

    //DFS染色
    memset(color, 0, sizeof(color));
    result = true;
    for (int i = 1; i <= n; ++ i) //每次找当前未染色的编号最小的结点,并染颜色1
        if (! color[i]) //当前位置尚未被染色
        {
            color[i] = 1;
            if (! dfs(i)) //染色时出现矛盾,此时图不是一个二分图,即无法分配到两个栈中
            {
                result = false; //记录无解
                break;
            }
        }

    if (! result) //无解
        printf("0");
    else //有解
    {
        //模拟求解
        now = 1;
        for (int i = 1; i <= n; ++ i)
        {
            //将当前数字压入对应的栈
            if (color[i] == 1)
                printf("a ");
            else
                printf("c ");
            stack[color[i]][0] ++;
            stack[color[i]][stack[color[i]][0]] = a[i]; //this will work even if stack[1][0] = 0

            //循环检查,如果可以的话就从栈顶弹出元素
            while (stack[1][stack[1][0]] == now || stack[2][stack[2][0]] == now)
            {
                if (stack[1][stack[1][0]] == now)
                {
                    printf("b ");
                    stack[1][0] --;
                }
                else if (stack[2][stack[2][0]] == now)
                {
                    printf("d ");
                    stack[2][0] --;
                }
                now ++;
            }
        }
    }
}

Written by sqybi

十一月 16th, 2008 at 5:14 下午

发生在我身上的作弊事件

with 26 comments

首先承认,我标题党了.
今天忘了带本本的电源线回家,借着电池写一点点,希望能坚持到写完甚至看几集柯南.

刚刚收到了来自某同学A的短信.说是ta的另一个同学B,学习成绩不好,高考可能连天大都考不上.大家又知道最近NOIP快到了,于是乎就不知道怎么打听到了我,想让我帮他把题目做出来然后拿短信发过去.
我自然是一口回绝了.

我和A并不是很熟识,但是我很确定A并不是一个多么坏的人,或者说是一个好孩子.其实对A帮B来求我的行为,也是挺理解的.理解归理解,让我同意这件事是肯定不可能的了.
也正因为这样,今天的文章里没有提到任何一个人的名字甚至与ta相关的一点资料.否则直接拉出来曝光,俩人就都会很惨.我不想那样,正如当时xw事件一样,每个人都不坏,不希望把谁一棒子打死.
我回复A,如果B现在拼高考,也不是没时间,估计以前B也把时间都花在玩游戏上了.
A只是回复我,ta当时的第一反应也是感到愤慨.

几年的经验告诉我,OI并不是对所有人都适合.毕竟是功利性太强的东西.
自己也是一个天津OI的前辈了,曾经看着身边的人们一个一个地倒下,最后所剩无几.最初的没感觉,到后来的害怕,到最后的没感觉.一个可怕的轮回.
那些倒下的人,并不是被谁谁谁打倒的.打倒他们的是自己,是自己的功利.

不得不承认的是,我搞OI也很功利.我一直很坚信,如果没有保送,我一定会扭头回去拼高考(这里说一下,请不要回复什么大学不是唯一的出路以及此类的文笔更好一些的语言).但实际上,我是很享受coding的感觉的.如果啥时候coder不是民工了,能赚钱了,我一定会去做一个coder.
但是见过很多,被功利这颗烟雷迷惑的人,就像CS里那些弱智bot一样.

想翻一篇貌似是WTommy写的blog,是写他在负责NOIP小学组评测的时候,和NOIP小学组的学生们的对话.但是没翻出来,只好凭着记忆打一些东西到这里.
“我给你钱了,你就得给我测出分来.”
-”为啥我没有分?” -”因为你的程序不对啊.” -”给我点分吧.” -”测出来没有分就没有了.” -”可是我有钱啊.”
请注意我加粗的那个词.

这个泥潭太深了,害怕自己有一天也会陷进去.但是,至少在现在还没有走出那一步的时候,多拉出来几个人吧.说不定,等到那一天,会有人把我拉出来的.

突然又想到了,一个最近看到的问题.
“国人的思想,有很多都是被别人灌输的,而没有自己的思想.”
不论这句话对错,一些父母想维护自己的”权威”,就已经是在做这件事了.很难想像,以后的人们都是一个个傀儡,自然也不愿去想象.

好了,回到正题.
我拒绝了A的请求,并不是像A所说的”捍卫这个比赛的公平性”这样伟大.只是觉得,抵制作弊这种事情人人都应该做到的,自己没有理由不去做.
或许如果没有xw事件,没有WTommy的那篇blog,我就会答应下来了呢.特别是xw事件,对我的影响很大.
记得最开始,许多人不相信,那封联名信能够起到什么作用.甚至OIBH上有帖子,讨论那封联名信是否会影响到在上面签字的人.
但是结果是,CCF重视了这次事件,最终给了我们一个比较满意的答复.于是,我从此开始信任身边的人们.
而当时在那封信上的签名,就成为了一种责任.既然亲手净化了天津OI,就不能自己再来污染它.

如果A看了这篇文章,希望你能看到上面这句话.这才是我拒绝你的真正原因.

附:当时在联名信上签名的人:
武斌(skywind,OIBH管理员,现武汉大学学生,对那次事件的解决做出了极大贡献) 杨博洋 邱堃 王乃岩(winsty,浙大学生,我哥) 朱健维 杨宗衡 段岩 李博 邹蒙川(ddeggzmc,当时的乐山外国语学校学生,现天津一中学生,很可爱的MM) 隋清宇(sqybi,本人) 刘智猷 王洋 秦黛(耀华学长) 康旭阳(SweetSc,新华中学学生) 卢光辉(WindyWinter,也是学长了,很有正义感和自己的思想的人) 张潇 舒昱扬

好吧,就侃到这里.貌似还能看两集柯南的说.
明天SRM, gl.

P.S.貌似现在的电量只能看一集了…

Written by sqybi

十一月 5th, 2008 at 8:33 下午

Posted in About Life

Tagged with , , , , ,

这年头,博客也有争议(附雷人课表一篇)

with 18 comments

首先这样一篇文章:<小学生热衷开博客引争议 专家:家长要正确引导孩子>.
底下一条评论很好的表达了我的想法:”以前我读书的时候,家里逼我写日记,老师逼我写日记,我不写就挨说…现在我弟弟主动写日记了,家里逼他不要写,老师也逼他不要写,写就挨说…这世界变化真快”(因为某些和谐的原因,原评论中的”逼”全部被替换为”文明用语”).

记得小学的时候,老师也逼我写日记,当时因为不愿意写次次被请家长.而感觉那时候写出来的东西,也根本不能算是日记.纯粹是滥竽充数.
而各种关于日记本被偷看的新闻,也让我至今为止没有写过一句日记.

我一直认为blog不能算日记,因为它的公开性不可能让人们在上面写出日记的内容(不排除有某些刚刚接触网络的小盆友写出来这种东西对此我只能表示无语以及对这些人最美好的祝福因为他们或许马上就要被家长OOXX掉了).
不过不可否认的是,我写过的最好的东西,都是在我写blog最频繁的时候产生的.
blog上的东西没有文体,没有格式,可以想到哪里写到哪里.但是如果你真的想写一篇好文章的时候,虽然它可能依然没有文体没有格式,这时你却需要去找很多能够支持自己观点的资料.通过论述论据进行论证啊,孩子们!

现在的家长太容易受某些xx媒体的诱惑,听人家宣传网络如何如何不好,于是稍微和网络沾边的东西都开始恐惧,恐惧,再恐惧.这里建议被恐惧的家长控制的小盆友们,学信息学奥利匹克竞赛吧,给自己一个名正言顺的接触信息时代的理由,也给自己的家长一个认识信息技术的好处的理由.
当然前提是你学信息学奥赛不是为了玩游戏,否则出了问题别找我,我担待不起.

再说游戏.首先对于网游,我基本没玩过,所以不好说会不会沉迷之类的.但是唯一肯定的一点是,现有的任何网游都无法让我提起兴趣,包括FIFA OL.而单机游戏,我一直玩了这么多年的只有FIFA一款,这也与EA每年一次的新版速率有关.总之,感觉如果一个人玩单机游戏都能沉迷,那么这个人即使不接触计算机,照样不可能成大事.所以做父母的还是别把所有责任都推到电脑上.想想当初电脑是谁买的,希望孩子用电脑做什么,而自己有没有从一开始去控制孩子做该做的事吧.
其实如果家长看待游戏不是像瘟疫一样,那么孩子也有责任向家长说明自己在做什么的说.比如我当初第一次玩FIFA就是当着我父母的面装上的(只可惜安装盘是坏的,所以一年之后才正式玩到FIFA).

好了,废话了不少,来点轻松的吧.
华中科技大学的Thity同学给我们带来了一款雷人的课表.他是计算机学院的(貌似是CS系?),然后课表的内容俨然是某政治大学的…

1—2 中国近现代史纲要 西十二楼 N211
3—4 微积分(一)上西十二楼 S109
5—6 军事理论 西十二楼 N203
7—8
9—10 信息技术导论  西十二楼 N203

1—2 思想道德修养与法律基础 西十二楼 S102
3—4 大学英语
5—6 线性代数    西十二楼 N203
7—8
9—11 形势与政策  西五楼 417

1—2 微积分(一)上  西十二楼 S109
3—4
5—6 计算机基础  南一楼 803
7—8
9—10 中国语文  西十二楼 S102

1—2
3—4 体育  西操场
5—6 工程制图(一)  西十二楼 N307 工程制图(一) 西十二楼 N308
7—8
9—12 思想道德修养与法律基础  西十二楼 S102

1—2
3—4 线性代数    西十二楼 N203
5—6 大学英语
7—8 微积分(一)上  西十二楼 S109

1—2 计算机科学与技术方法论    西五楼 217
3—4 计算机科学与技术方法论    西五楼 217
5—6 计算机科学与技术方法论    西五楼 217
7—8 计算机科学与技术方法论    西五楼 217

于是被彻底雷倒.

和计算机有关的只有这三门课:信息技术导论,计算机基础,计算机科学与技术方法论.注意最后一门是”方法论”!
下面看几段聊天记录:

Thity 08:56:39
马列什么的下学期开
Killa.sqybi 08:56:47
这个哪些是选的课..
Thity 08:56:57
大一上学期没有选课
Thity 08:57:00
都得上

Thity 08:57:08
最可恶的是星期六要上一天一样的课
Killa.sqybi 08:57:16
…….看到了………
Killa.sqybi 08:57:21
难道不会恶心死…
Thity 08:57:31
这课一看名字就知道一定很恶心
Thity 08:57:34
还要上一天…
Thity 08:57:45
还要占用美好的星期六

Killa.sqybi 09:01:03
还好没开普物
Thity 09:01:20
咦 怎么没有物理!
Thity 09:01:46
可是发的教材有物理…
Thity 09:01:54
估计是下学期开物理

Killa.sqybi 09:02:12
好吧 貌似这个大学是XX中学附大

Killa.sqybi 09:02:46
感觉咱们的对话应该在GTalk上进行 然后我贴到blog上 免得受到某些鄙视QQ的人的鄙视
Thity 09:03:32
我觉得装Gtalk比较麻烦
Thity 09:03:44
让他们BS去吧

好了,今天的行动到此为止.
能完整的吹完爱尔兰之歌了,yeah.

Written by sqybi

十月 2nd, 2008 at 9:25 上午

Posted in About Life

Tagged with , , , , , , , ,

ZOJ Monthly August 2008 and SRM 414 and dd’s Contest

with 10 comments

今天做了两场比赛,ZOJ Monthly和SRM.
总体感觉还是不错,coding速度有待提高.思路准确性不够.coding准确性尚可.
ZOJ去晚了,做了几道弱智题,拿了rank 60.其实早去半小时,就能top 25了…
ZOJ月赛好多人组队参加…下次考虑和jl等人组个队.
SRM,基本发挥水平,250+500全部Pass,可惜250和500开始想的都有问题,加上我C++的coding速度很慢,最后加起来也不到500分.我那个room都是强人,六个red…于是cha人的时候,打开一个代码,就提示我这个已经被cha了,再打开一个,又提示…最后一个代码也没看完就都cha干净了…现在rating是1671,涨得不多…

对了,还有dd的一个模拟赛,那个我写了三道题的标程.交到TOI的时候,第三题又因为%lld和%I64d的问题挂掉了…我忘了TOI是Linux系统,直接交了%I64d…

ZOJ Monthly

中午去图书大厦,下午回到家,winsty告诉我有月赛…然后那时候人家都比了一半了.
这次月赛是ZOJ内部集训的题目,于是某道题我就得利了…可是重写的程序精度还是出了问题,最后2Y掉.就是1008题,这题AC率好低…传说中的万人坑.
然后1006,1002和1001都1Y掉了.

1008
这题注意精度,十分注意精度…没别的了.

1006
统计每个数在a和b中分别出现的次数,然后对每个数对应的两个次数取个最小值,最后把这些最小值都+起来.用了map和迭代器,STL果然好用.

1002
小学奥数题,竟然想了半天没想出来…后来MM群里有人提醒我(HL牛?),就是那个一个人牵着一匹马(一头驴也一样…)从A走到B途中要去河里喝水…问最短路径…后来又扯到Fermat原理…但不管怎么说,就是作(b, 0)关于三角形那条斜边的对称点…然后对称点和(0, a)连线,求出这条连线与斜边的交点,就是反射点…没有精度问题.

1001
写个暴力,跑,然后打表…dd的题目真恶搞…

SRM 414 Div 1

第一次做Div 1,题目还不是很难.

250
这道题一开始想复杂了,一直在想怎么处理第一个表格从一天的什么时刻开始填的问题…后来发现一天最多有10^6个时刻,于是直接枚举…然后最后Pass掉.很囧…这道题白白掉了一大半的分数.

500
这道题开始想的就是正确的,贪心.可是细节处理出了问题.
算法是每个string记录当前取到了哪个字母,每次取字典序最小的一个字母.
细节上,当两个字符串当前字母一样的时候,需要比较剩余后缀的字典序.注意abc比abcd的字典序靠后,而不是一般我们认为的靠前.
一度以为时间复杂度太高会被cha,事实证明pass了.

dd_engi的NOIP模拟赛

题目难度适中.dd的题质量还是不错的.

生命游戏
纯模拟…没什么可说的,判断的时候注意边界.还有n和m的输入顺序,是先m后n.

正则表达式简化版
还是模拟…很恶心的题,这题我没写标程.

魔法塔防
这套题最有意思的一道题!很容易想到n^3的DP,其实只需要证明一点:红色的一定在最后.尝试反证法.

公司聚会
很裸的Tree DP.我写了多叉转二叉,不过dd的标程写法很独特.详情请期待dd的题解.

Written by sqybi

八月 17th, 2008 at 2:18 上午

Posted in About Computer, [OI, ACM, etc]

Tagged with , , , ,

SQYBI.com 建成

with 37 comments

经过一个晚上加一个上午的奋战,SQYBI.com终于建成了.
先撒花个~

自从保送之后,就只是在写程序.NOI前说保送之后要好好去玩一玩的,可现在倒好,连玩什么都不知道了.Nxun在市内的时候,我还能找他打几次台球;现在Nxun已经到蓟县了,同学们也都忙于高三的复习了,就剩我一个人在这里无聊.
“没事做就会无聊 没有地方去动手动脚 闷到就快要发烧 嘿咻嘿咻冲冷水澡” — 林俊杰<无聊>
冷水澡在绍兴一中倒是冲了不少,可还是无聊.

然后呢,似乎是alft提醒了我一件事–一件我早就想做的事–买个空间,搭个WordPress.
于是到处找合租的人,凑齐到3个的时候找到了dd_engi牛,希望他帮忙组织.很可惜的是,据winsty介绍,dd_engi由于要给队伍写模板,所以几乎没有时间.然后dd几天都没上线.
最后和Nxun决定,自己组织合租.总共七个人,拍卖域名,签协议,汇款(还没把钱给鱼牛呢…),折腾了好久,终于买到了空间.
接下来开始了费劲的调试,今天凌晨更是为了跟DreamHost协商关于域名的事情一直到早晨四点半才睡觉.不过DreamHost的客服真的很不错,赞一个先.

接下来就是搭Blog,做主页.Logo是早就做好的,不过那个Flash还要多谢wh帮忙.wh帮我做了个Flash,然后我又按照他的做法重新做了一个,然后就成了现在主页那样.
忘了说,主页的音乐是梁静茹的<满满的都是爱>.
嗯,Logo呢,是用AAA Logo做的(话说dog同学一眼就看出来了),制作理念有两个,一是简洁,一是可爱,给一些人看过,他们的反应是这两点基本都做到了.我也比较满意.

写到这里,突然起风了,这个夏天第一次这么凉快.

这个site用来干啥呢?基本还是一个个人站,不过题解之类的都会放到这一个站点里.也就是说,会把NestingEggNOT A BLOG合并到这里.不过并没有搬家的打算,一切重新开始.

正式转战ACM了,一些C++学习心得或者SRM等比赛的题解之类的也会扔到这里.另,我已经正式停用Pascal,一切Pascal编译器都卸掉了–希望能转的彻底.不过现在用C++还是会出现各种dd牛看来很弱智的错误.慢慢熟悉吧.
第一次SRM,Div 2的20多名,然后就变黄了.希望16号晚上12点,第一次Div 1,rating别掉得太快就好…

虽然转战ACM,但借用zch的那句话–做永不退役的OIer.

Fighting forever.

Written by sqybi

八月 14th, 2008 at 6:16 下午

Posted in About Life

Tagged with , , , ,