海清's profileCockhorse BlogPhotosBlogListsMore Tools Help

Blog


    August 27

    00-07冬令营论文索引

    也许复习可以用到
     
    ----------------------------------------------------------------------------------------
    2000

    陈彧                信息学竞赛中的思维方法
    方奇                动态规划
    高寒蕊                递推关系的建立及在信息学竞赛中的应用
    郭一                数学模型及其在信息学竞赛中的应用
    江鹏                探索构造法解题模式
    李刚                动态规划的深入讨论
    龙翀                解决空间规模问题的几种常用的存储结构
    骆骥                数学模型的建立和选择
    施遥                人工智能在围棋程序中的应用
    肖洲                数据结构的在程序设计中的应用
    谢婧                规模化问题的解题策略
    徐串                论程序的调试技巧
    徐静                图论模型的建立与转化
    杨江明                论数学策略在信息学问题中的应用
    杨培                非最优化算法初探
    张辰                动态规划的特点及其应用
    张力                类比思想在解题中的应用
    张一飞                冗繁削尽留清瘦——浅谈信息的充分利用


    ----------------------------------------------------------------------------------------
    2001

    符文杰                Pólya原理及其应用
    高寒蕊                从圆桌问题谈数据结构的综合运用
    高岳                中等硬度解题报告
    江鹏                从一道题目的解法试谈网络流的构造与算法
    李益明                计算几何
    李源                树的枚举
    刘汝佳                搬运工问题的启示
    骆骥                由“汽车问题”浅谈深度搜索的一个方面———搜索对象与策略的重要性
    毛子青                动态规划算法的优化技巧
    俞玮                基本动态规划问题的扩展
    张一飞                求N!的高精度算法


    ----------------------------------------------------------------------------------------
    2002

    戴德承                退一步海阔天空——“目标转化思想”的若干应用
    方奇                浅谈必要条件的应用
    符文杰                排序网络
    何江舟                用高斯消元法解线性方程组
    何林                猜想及其应用
    黄芸                POI0110  跳舞蝇
    金恺                浅谈网络流算法的应用
    李澎煦                半平面交的算法及其应用
    李睿                二分法与统计问题
    骆骥                浅析解 “对策问题” 的两种思路
    孙方成                偶图的算法及应用
    孙林春                让我们做得更好——从《parity》的解法谈程序优化
    王知昆                搜索顺序的选择
    许智磊                二分,再二分!――从Mobiles(IOI 2001)一题看多重二分
    杨旻旻                构造法——解题的最短路径
    俞玮                Ulam的游戏和编码
    张家琳                多项式乘法
    张宁                遗传算法的特点及其应用
    张一飞                由感性认识到理性认识——透析一类搏弈游戏的解答过程
    周文超                树结构在程序设计中的运用


    ----------------------------------------------------------------------------------------
    2003

    方奇                染色法和构造法在棋盘上的应用
    高正宇                答案只有一个——浅谈问答式交互问题
    何林                一类称球问题的解法
    侯启明                信息论在信息学竞赛中的简单应用
    姜尚仆                模线性方程的应用——用数论方法解决整数问题
    金恺                探寻深度优先搜索中的优化技巧——从正方形剖分问题谈起
    雷环中                结果提交类问题
    林希德                求最大重复子串
    刘才良                平面图在信息学中的应用
    刘一鸣                一类搜索问题的优化思想——数据的有序化
    陆可昱                长方体的体积并
    饶向荣                病毒的DNA———剖析一道字符匹配问题解析过程
    邵烜程                数学思想助你一臂之力
    王知昆                浅谈用极大化思想解决最大子矩形问题
    伍昱                由对称性解2-SAT问题
    项荣璟                充分利用问题性质——例析动态规划的“个性化”优化
    许智磊                浅谈补集转化思想在统计问题中的应用
    张宁                猜数问题的研究——《聪明的学生》一题的推广
    张云亮                论对算法的选择
    周源                浅析“最小表示法”思想在字符串循环同构问题中的应用


    ----------------------------------------------------------------------------------------
    2004

    贝小辉                浅析树的划分问题
    鬲融                浅谈特殊穷举思想的应用
    韩文韬                论C++语言在信息学竞赛中的应用
    何林                信息学中守恒法的应用
    胡伟栋                减少冗余与算法优化
    黄源河                浅谈图论模型的建立与应用
    金恺                极限法——解决几何最优化问题的捷径
    李锐喆                细节——不可忽视的要素
    栗师                转化目标在解题中的应用
    林涛                线段树的应用
    楼天城                匹配算法在搜索问题中的应用
    汪汀                最小生成树问题的拓展
    吴景岳                最小生成树算法及其应用
    肖天                “分层图思想”及其在信息学竞赛中的应用
    许智磊                后缀数组
    薛矛                解决动态统计问题的两把利刃 ——剖析线段树与矩形切割
    杨思雨                伸展树的基本操作与应用
    周源                浅谈数形结合思想在信息学竞赛中的应用
    朱晨光                优化,再优化!——从《鹰蛋》一题浅析对动态规划算法的优化
    朱泽园                多串匹配算法及其启示


    ----------------------------------------------------------------------------------------
    2005

    何林                数据关系的简化
    胡伟栋                浅析非完美算法在信息学竞赛中的应用
    黄刚                数据结构的联合
    黄源河                左偏树的特点及其应用
    蒋炎岩                数据结构的联合——块状链表
    金恺                杂题大拼盘
    李羽修                Hash函数的设计优化
    栗师                树的乐园——一些与树有关的题目
    龙凡                序的应用
    潘震皓                置换群快速幂运算 研究与探讨
    钱自强                遗传算法应用的分析与研究
    任恺                图论的基本思想及方法
    唐文斌                正难则反–浅谈逆向思维在解题中的应用
    汪汀                参数搜索的应用
    王俊                浅析二分图匹配在信息学竞赛中的应用
    吴景岳                解法讨论
    杨俊                二分策略在信息学竞赛中的应用
    杨思雨                美,无处不在——浅谈“黄金分割”和信息学的联系
    杨弋                从《小H的小屋》的解法谈算法的优化
    张伟达                用改进算法的思想解决规模维数增大的问题
    周源                压去冗余 缩得精华——浅谈信息学竞赛中的“压缩法
    朱晨光                浅析倍增思想在信息学竞赛中的应用
    朱泽园                回到起点——一种突破性思维


    ----------------------------------------------------------------------------------------
    2006

    陈启峰                一张一弛,解题之道——“约制、放宽”方法在解题中的应用
    陈首元                维护森林连通性——动态树
    冯威                数与图的完美结合-------浅析差分约束系统
    高逸涵                对于一道题目的深入分析--对猴子分桃问题的延伸
    黄劲松                贪婪的动态规划——浅谈贪心思想在动态规划中的应用
    黄晓愉                信息学竞赛中搜索问题的常见优化技巧
    贾由                由图论问题浅析算法优化
    李天翼                从特殊情况考虑
    龙凡                一类猜数问题的研究
    汤泽                从一类单调性问题看算法的优化
    唐文斌                浅谈“调整”思想在信息学竞赛中的应用
    汪晔                信息学中的参考系与坐标系
    王栋                浅析平面Voronoi图的构造及应用
    王赟                Trie图的构建、活用与改进
    余远铭                最短路算法及其应用
    俞鑫                棋盘中的棋盘——浅谈棋盘的分割思想
    周戈林                浅谈类比思想
    周以苏                论反汇编在时间常数优化中的应用
    朱晨光                基本数据结构在信息学竞赛中的应用
    朱泽园                半平面交的新算法及其实用价值


    ----------------------------------------------------------------------------------------
    2007

    高逸涵         与圆有关的离散化
    王晓珂         解析一类组合游戏
    仇荣琦         欧拉回路性质与应用探究
    余江伟         如何解决动态统计问题                                                                       
    杨  沐         浅析信息学中的“分”与“合”
    李宇骞         浅谈信息学竞赛中的线性规划——简洁高效的单纯形法实现与应用
    袁昕颢         动态树及其应用
    杨  哲         凸完全单调性的加强与应用
    王欣上         浅谈基于分层思想的网络流算法
    陈启峰         Size Balanced Tree
    杨  弋         Hash在信息学竞赛中的一类应用
    古 楠         平面嵌入
    郭华阳         RMQ与LCA问题
    刘雨辰         对拟阵的初步研究
    陈  雪         问题中的变与不变
    何 森         浅谈数据的合理组织
    胡伯涛         最小割模型在信息学竞赛中的应用
    陈瑜希         多角度思考创造性思维——运用树型动态规划解题的思路和方法探析
    周  冬         生成树的计数及其应用
    刘家骅         浅谈随机化在信息学竞赛中的应用


    ----------------------------------------------------------------------------------------
     

    P1160

    坦克大战这道题真是做郁闷了,一道平衡树的题,我写了Splay、Treap、至顶向下伸展树、线段树但是都过不到,caesius还写了SBT他也没过,也不知道为什么,没办法vijos又不能给数据,这道题数据太大又不可能把它骗出来,或许是我们的RP还不够吧。 

    P1083

    一道很经典的线段树的题。 
    August 26

    递推式的矩阵运算

    形如f[n]=x1*f[n-1]+x2*f[n-2]+....+xk*f[n-k](xi为实数)的递推式
    用一个k*1的矩阵表示相邻的k个数
    可以通过   x1,x2,x3,...,xk-1,xk          f[n-1]          f[n]          来计算f[n]               
                  1 ,0 ,0  ,...,  0  ,0           f[n-2]          f[n-1]                     
                  0 ,1 ,0  ,...,  0  ,0           f[n-3]          f[n-2]                       
                  ...                          ×    ..         =    ..                    
                  ...                                ..                ..                  
                  0 ,0 ,0  ,...,  1  ,0           f[n-k]          f[n-(k-1)]                             
                    (k*k的矩阵)              (k*1的矩阵)                                
    当然这样计算看起来很复杂,其实矩阵的优点就在于它可以二分计算
    由于矩阵运算遵循结合律,所以可以先把k*k的矩阵自乘若干次后再和前k个数组成的k*1的矩阵相乘,则可得到f[n],算一个矩阵的n次幂可通过二分快速幂的方法求解,这样的话时间复杂度就只有O(k^2logn)。 
    August 22

    LIS的O(nlogn)算法

    var
        a:array[1..10000]of longint;
        w,i,j,k,n:longint;
    function find(c,d,x:longint):longint;
    begin
        if c>d then
            exit(c);
        if c=d then
            if x>a[c] then
                exit(c+1)
            else
                exit(c);
        if x>a[(c+d)div 2] then
            exit(find((c+d)div 2+1,d,x))
        else
            exit(find(c,(c+d)div 2,x));
    end;{二分查找}
    begin
        readln(n);
        for i:=1 to n do
        begin
            read(k);
            j:=find(1,w,k);
            if j>w then
                inc(w);
            a[j]:=k;
        end;
        writeln(w);
    end.
     

    线段树、Splay、Treep的时间比较

    Problem : inverse
    逆序对
    问题描述
        在一个排列中,前面出现的某个数比它后面的某个数大,即当Ai>Aj且i<j时,则我们称Ai和Aj为一个逆序对。给出一个1到N的排列,编程求出逆序对的个数。
    输入格式
        第一行输入一个正整数N;
        第二行有N个用空格隔开的正整数,这是一个1到N的排列。
    输出格式
        输出输入数据中逆序对的个数。
    样例输入
    4
    3 1 4 2
    样例输出
    3
    样例说明
        在输入数据中,(3,1)、(3,2)和(4,2)是仅有的三个逆序对。
    数据规模
        对于30%的数据,N<=1000;
        对于100%的数据,N<=100 000。 
     
    测了10个数据,还是线段树最快:
     
    代码:
     
    线段树:
    var
        f:array[1..200000]of longint;
        a:array[1..100000]of longint;
        i,n,o:longint;
        mo:int64;
    procedure go(x,i,c,d:longint);
    begin
        if c<d then
            if x<=(c+d) div 2 then
                go(x,i*2,c,(c+d)div 2)
            else
            begin
                inc(mo,f[i*2]);
                go(x,i*2+1,(c+d)div 2+1,d);
            end;
        inc(f[i]);
    end;
    begin
        readln(n);
        for i:=1 to n do
            read(a[i]);
        fillchar(f,sizeof(f),0);
        o:=1;
        while o<n do
            o:=o*2;
        mo:=0;
        for i:=n downto 1 do
            go(a[i],1,1,o);
        writeln(mo);
    end.
     
     
    Treap:
    type
        tree=^rec;
        rec=record
            left,right:tree;
            x,ld,rd:longint;
            e:double;
        end;
    var
        h:tree;
        i,j,k,l,n,m:longint;
        o:int64;
    function left(var p:tree):tree;
    var
        t:tree;
    begin
        t:=p^.left;p^.left:=t^.right;t^.right:=p;
        inc(t^.rd,p^.rd+1);
        dec(p^.ld,t^.ld+1);
        exit(t);
    end;
    function right(var p:tree):tree;
    var
        t:tree;
    begin
        t:=p^.right;p^.right:=t^.left;t^.left:=p;
        inc(t^.ld,p^.ld+1);
        dec(p^.rd,t^.rd+1);
        exit(t);
    end;
    function insert(x:longint;var p:tree):tree;
    begin
        if p=nil then
        begin
            new(p);
            p^.x:=x;p^.e:=random;p^.ld:=0;p^.rd:=0;p^.left:=nil;p^.right:=nil;
            exit(p);
        end;
        if x<p^.x then
        begin
            inc(o,p^.rd+1);
            p^.left:=insert(x,p^.left);
            inc(p^.ld);
            if p^.left^.e<p^.e then
                p:=left(p);
        end
        else
        begin
            p^.right:=insert(x,p^.right);
            inc(p^.rd);
            if p^.right^.e<p^.e then
                p:=right(p);
        end;
        exit(p);
    end;
    begin
        randomize;
        readln(n);
        o:=0;
        for i:=1 to n do
        begin
            read(k);
            h:=insert(k,h);
        end;
        writeln(o);
    end.

     
    Splay:
    type
        tree=^rec;
        rec=record
            ld,rd,x:longint;
            left,right:tree;
        end;
    var
        h:tree;
        n,i,k:longint;
        o:int64;
    function left(a:tree):tree;
    var
        b:tree;
    begin
        b:=a^.left;a^.left:=b^.right;b^.right:=a;
        inc(b^.rd,a^.rd+1);
        dec(a^.ld,b^.ld+1);
        exit(b);
    end;
    function right(a:tree):tree;
    var
        b:tree;
    begin
        b:=a^.right;a^.right:=b^.left;b^.left:=a;
        inc(b^.ld,a^.ld+1);
        dec(a^.rd,b^.rd+1);
        exit(b);
    end;
    function leftleft(a:tree):tree;
    var
        b,c:tree;
    begin
        b:=a^.left;a^.left:=b^.right;b^.right:=a;
        c:=b^.left;b^.left:=c^.right;c^.right:=b;
        inc(b^.rd,a^.rd+1);
        inc(c^.rd,b^.rd+1);
        dec(a^.ld,b^.ld+1);
        dec(b^.ld,c^.ld+1);
        exit(c);
    end;
    function rightright(a:tree):tree;
    var
        b,c:tree;
    begin
        b:=a^.right;a^.right:=b^.left;b^.left:=a;
        c:=b^.right;b^.right:=c^.left;c^.left:=b;
        inc(b^.ld,a^.ld+1);
        inc(c^.ld,b^.ld+1);
        dec(a^.rd,b^.rd+1);
        dec(b^.rd,c^.rd+1);
        exit(c);
    end;
    function leftright(a:tree):tree;
    var
        b,c:tree;
    begin
        b:=a^.left;c:=b^.right;
        a^.left:=c^.right;c^.right:=a;
        b^.right:=c^.left;c^.left:=b;
        a^.ld:=c^.rd;
        b^.rd:=c^.ld;
        inc(c^.rd,a^.rd+1);
        inc(c^.ld,b^.ld+1);
        exit(c);
    end;
    function rightleft(a:tree):tree;
    var
        b,c:tree;
    begin
        b:=a^.right;c:=b^.left;
        a^.right:=c^.left;c^.left:=a;
        b^.left:=c^.right;c^.right:=b;
        a^.rd:=c^.ld;
        b^.ld:=c^.rd;
        inc(c^.ld,a^.ld+1);
        inc(c^.rd,b^.rd+1);
        exit(c);
    end;
    function insert(x:longint;var p:tree):tree;
    begin
        if p=nil then
        begin
            new(p);
            p^.x:=x;p^.ld:=0;p^.rd:=0;p^.left:=nil;p^.right:=nil;
            exit(p);
        end;
        if x<p^.x then
        begin
            inc(o,p^.rd+1);
            inc(p^.ld);
            p^.left:=insert(x,p^.left);
            if p^.left^.x=x then
            begin
                if p=h then
                p:=left(p);
            end
            else
            if x<p^.left^.x then
                p:=leftleft(p)
            else
                p:=leftright(p);
        end
        else
        begin
            inc(p^.rd);
            p^.right:=insert(x,p^.right);
            if p^.right^.x=x then
            begin
                if p=h then
                p:=right(p);
            end
            else
            if x>p^.right^.x then
                p:=rightright(p)
            else
                p:=rightleft(p);
        end;
        exit(p);
    end;
    begin
        readln(n);
        for i:=1 to n do
        begin
            read(k);
            h:=insert(k,h);
        end;
        writeln(o);
    end.
     
     
    至顶向下伸展树:
    type
        tree=^rec;
        rec=record
            father,left,right:tree;
            ld,rd,x:longint;
        end;
    var
        h:tree;
        i,j,k,l,n,m:longint;
        o:int64;
    function left(var p:tree):tree;
    var
        t:tree;
    begin
        t:=p^.left;p^.left:=t^.right;t^.right:=p;
        inc(t^.rd,p^.rd+1);
        dec(p^.ld,t^.ld+1);
            exit(t);
    end;
    function right(var p:tree):tree;
    var
        t:tree;
    begin
        t:=p^.right;p^.right:=t^.left;t^.left:=p;
        inc(t^.ld,p^.ld+1);
        dec(p^.rd,t^.rd+1);
        exit(t);
    end;
    function insert(x:longint;var p:tree):tree;
    begin
        if p=nil then
        begin
            new(p);
            p^.x:=x;p^.left:=nil;p^.right:=nil;p^.ld:=0;p^.rd:=0;
            exit(p);
        end;
        if x<p^.x then
        begin
            inc(o,p^.rd+1);
            inc(p^.ld);
            p^.left:=insert(x,p^.left);
        end
        else
        begin
            inc(p^.rd);
            p^.right:=insert(x,p^.right);
        end;
        exit(p);
    end;
    function splay(x:longint;t:tree):tree;
    var
        p,head,leftmax,rightmin:tree;
    begin
        new(head);
        leftmax:=nil;rightmin:=nil;
        while x<>t^.x do
        if x<t^.x then
        begin
            if x<t^.left^.x then
                t:=left(t);
            if rightmin<>nil then
                rightmin^.left:=t
            else
                head^.right:=t;
            t^.father:=rightmin;
            rightmin:=t;
            t:=t^.left;
        end
        else
        begin
            if x>t^.right^.x then
                t:=right(t);
            if leftmax<>nil then
                leftmax^.right:=t
            else
                head^.left:=t;
            t^.father:=leftmax;
            leftmax:=t;
            t:=t^.right;
        end;
        if rightmin<>nil then
        begin
            rightmin^.left:=t^.right;
            p:=rightmin;
            while p<>nil do
            begin
                if p^.left<>nil then
                    p^.ld:=p^.left^.rd+p^.left^.ld+1
                else
                    p^.ld:=0;
                p:=p^.father;
            end;
            t^.right:=head^.right;
            inc(t^.rd,head^.right^.ld+head^.right^.rd+1);
        end;
        if leftmax<>nil then
        begin
            leftmax^.right:=t^.left;
            p:=leftmax;
            while p<>nil do
            begin
                if p^.right<>nil then
                    p^.rd:=p^.right^.ld+p^.right^.rd+1
                else
                    p^.rd:=0;
                p:=p^.father;
            end;
            t^.left:=head^.left;
            inc(t^.ld,head^.left^.ld+head^.left^.rd+1);
        end;
        exit(t);
    end;
    begin
        readln(n);
        for i:=1 to n do
        begin
            read(k);
            h:=insert(k,h);
            h:=splay(k,h);
        end;
        writeln(o);
    end.

    高精乘法万进制

    var
        a,b,c:array[1..20000]of longint;
        k,i,j,k1,k2,l,p,o:longint;
        m:array[1..4]of longint;
        s1,s2:ansistring;
    begin
        readln(s1);
        readln(s2);
        l:=0;i:=length(s1);k1:=0;
        while i>0 do
        begin
            inc(l);
            if (l=4)or(i=1) then
            begin
                inc(k1);a[k1]:=0;
                for j:=i to i+l-1 do
                a[k1]:=a[k1]*10+ord(s1[j])-48;
                l:=0;
            end;
            dec(i);
        end;
        l:=0;i:=length(s2);;k2:=0;
        while i>0 do
        begin
            inc(l);
            if (l=4)or(i=1) then
            begin
                inc(k2);b[k2]:=0;
                for j:=i to i+l-1 do
                    b[k2]:=b[k2]*10+ord(s2[j])-48;
                l:=0;
            end;
            dec(i);
        end;
        a[k1+1]:=0;b[k2+1]:=0;
        for i:=1 to k1+1 do
        begin
            p:=0;
            for j:=1 to k2+1 do
            begin
                o:=a[i]*b[j]+c[i+j-1]+p;
                p:=o div 10000;
                c[i+j-1]:=o mod 10000;
            end;
        end;
        l:=k1+k2;
        if c[l]=0 then
            dec(l);
        for i:=l downto 1 do
        begin
            fillchar(m,sizeof(m),0);
            k:=c[i];j:=0;
            while k<>0 do
            begin
                inc(j);
                m[j]:=k mod 10;
                k:=k div 10;
            end;
            if i<>l then
            for k:=4 downto 1 do
                write(m[k])
            else
            for k:=j downto 1 do
                write(m[k]);
        end;
        writeln;
    end. 

    LCIS(最长公共上升子序列)的O(nm)算法 (转)

    (转至 matrix67.com)  
     
        这里要说的这个算法利用了nlogn的最长上升子序列(LIS)的技巧:用f[k]表示长度为k的上升子序列最后一个数最小是多少。
        在最长公共上升子序列中,令f[i,j][k]表示A串前i个数字,B串前j个数字,长度为k的公共上升子序列中,最后一个数最小是多少。

        当A[i]=B[j]时,像nlogn的最长上升子序列一样把A[i]插入到f[i-1,j]中,这需要线性的时间扫一遍f[i,j];
        当A[i]<>B[j]时,我们需要合并f[i-1,j]和f[i,j-1],使得对于每个k满足f[i,j][k]:=min{ f[i-1,j][k],f[i,j-1][k] }。这需要线性的时间扫一边f[i-1,j]和f[i,j-1]并取k相同时的较小值。
        最后输出f[n,m]的长度(使f[n,m][k]有意义的最大的k)。
        这样的复杂度是三方的,我们需要优化。

        考虑A[i]=B[j]的情况。当i固定时,随着j的增加,插入的位置一定也在后移,因为同样是插入的A[i],但j的增加(B串长度的增加)使得f [i,j]更优,因此可以更新的值就更靠后。于是,对于每个i,我们可以按照k的顺序扫描f[i-1,j][k] 并在A[i]可以插入f[i-1][j]的k位置时增加j,从而预处理所有A[i]=B[j]时A[i]应该插入的位置。
        再考虑A[i]<>B[j]的情况。从定义看,f[i-1,j-1]和f[i-1,j]只有一个地方不一样,因为多一个数最多只能造成一个k 的值变小;同样地,f[i-1,j-1]和f[i,j-1]也只有一个地方不一样。因此,f[i-1,j]和f[i,j-1]最多只有两个k所对应的值不相同,且当有两个不同的值时,总是f[i-1,j]中的某个值较小,f[i,j-1]中的某个值较小。这给我们优化的余地。在每次处理完f[i,j]时,我们可以记录一个值x[i,j]表示f[i,j][k]与f[i-1,j][k]中值不一样的k是多少,在A[i]=B[j]时直接赋值为插入的位置,在 A[i]<>B[j]时待后文说明。以后合并时,先让f[i,j]:=f[i-1,j](由于此时的f[i-1,j]已经没有别的用处了,因此可以用滚动数组记录,直接令f[i-1,j]是f[i,j],避免实际的赋值操作),然后将新的f[i,j]中的,使f[i,j-1][k]比f[i- 1, j][k]小的k所对应值更新。这个k是多少呢?显然应该是x[i,j-1]。这样的操作同时可以确定x[i,j]=x[i,j-1]。
        这样,复杂度就达到了平方。
     

    次小生成树O(n^2)算法

    (vijos p1070)

    var
        f:array[1..500]of boolean;
        h:array[1..500,1..500]of boolean;{mintree}
        g,mt:array[1..500]of int64;{g[i]表示dijkstra过程中i所接的点(它们组成最小生成树的某条边),mt[i]表示dijkstra过程中i点到集合的最小值(具体见dijkstra算法)}
        cost,a:array[1..500,1..500]of int64;{cost[i,j]表示边的权,a[i,j]表示最小生成树上i到j联通路上最大边的权值}
        i,j,k,n,m:longint;
        o,min:int64;{o表示最小生成树的值,min表示次小生成树的值}
        y:boolean;

    {深搜过程} 
    procedure go(l,i:longint;o:int64);
    var
        j:longint;
    begin
        for j:=1 to n do
        if (h[i,j])and(not f[j]) then
        begin
            f[j]:=true;
            if cost[i,j]>o then
            begin
                a[l,j]:=cost[i,j];a[j,l]:=a[l,j];
                go(l,j,cost[i,j]);
            end
            else
            begin
                a[l,j]:=o;a[j,l]:=o;
                go(l,j,o);
            end;
        end;
    end;

    {主过程}
    begin
        {读入}
        readln(n,m);
        for i:=1 to n do
        for j:=1 to n do
            cost[i,j]:=maxlongint;
        for i:=1 to m do
        begin
            read(j,k);
            readln(cost[j,k]);
            cost[k,j]:=cost[j,k];
        end;
     
        {初始化}
        for i:=1 to n do
        begin
            mt[i]:=cost[1,i];
            g[i]:=1;
        end;
        fillchar(f,sizeof(f),0);f[1]:=true;
        o:=0;
     
        {dijkstra求最小生成树}
        for i:=1 to n-1 do
        begin
            min:=maxlongint;
            for j:=1 to n do
            if (not f[j])and(mt[j]<min) then
            begin
                min:=mt[j];k:=j;
            end;
            if min=maxlongint then
            begin
                writeln('Cost: -1');writeln('Cost: -1');
                halt;
            end;
            f[k]:=true;inc(o,min);
            for j:=1 to n do
            if (not f[j])and(cost[j,k]<mt[j]) then
            begin
                mt[j]:=cost[j,k];g[j]:=k;
            end;
        end;
     
        {在最小生成树上的边}
        for i:=2 to n do
        begin
            h[i,g[i]]:=true;h[g[i],i]:=true;
        end;
     
        {深搜预处理算出不在最小生成树上边的两个点联通路上的最大边(生成树也是联通图)}
        for i:=1 to n do
        begin
            fillchar(f,sizeof(f),0);f[i]:=true;
            go(i,i,0);
        end;
     
        {枚举所有不在最小生成树上的所有边,加上这条边再去掉这两个点联通路上的最大边,产生的新的生成树的值取最小值即为次小生成树}
        min:=maxlongint;
        for i:=1 to n-1 do
        for j:=i+1 to n do
        if (cost[i,j]<maxlongint)and(not h[i,j])and(o+cost[i,j]-a[i,j]<min) then
            min:=o+cost[i,j]-a[i,j];
     
        {输出}     
        write('Cost: ');
        if min=maxlongint then
            writeln(-1){表示无次小生成树}
        else writeln(min);
    end.

    高一寒假选拔赛前的学习内容

    (也可以当今后复习的目录)

    信息学与计算机语言的发展(香农与信息论,图灵与图灵机,冯·诺伊曼与逻辑门,机器码,汇编语言,高级语言,面向对象的语言,开源化与多平台)
    时间复杂度(渐近时间复杂度的严格定义,NP问题,时间复杂度的分析方法,主定理)
    排序算法(平方排序的应用,Shell排序,快速排序,归并排序,时间复杂度下界,线性时间排序,外部排序)
    数论(整除,集合论,关系,素数,进位制,辗转相除,扩展的辗转相除,同余运算,^解线性同余方程,中国剩余定理^)
    指针(链表,搜索判重,邻接表,开散列,二叉树的表示,多叉树的表示)
    按位运算(and,or,xor,shl,shr,一些应用)
    图论(图论模型的建立,平面图,欧拉公式与五色定理,求强连通分量,求割点和桥,^欧拉回路,求最短环^,AOV问题,AOE问题,最小生成树的三种算法,最短路的三种算法,标号法,^差分约束系统^,验证二分图,Konig定理,匈牙利算法,^KM算法,稳定婚姻系统^,最大流算法,最小割最大流定理,最小费用最大流算法)
    计算几何(平面解几及其应用,向量,点积及其应用,叉积及其应用,判断点是否在多边形内,半平面相交,^求点集的凸包^,最近点对问题,凸多边形的交,Voronoi图与Denaunay三角形剖分,离散化与扫描)
    数据结构(广度优先搜索,验证括号匹配,^表达式计算^,递归的编译,Hash表,分段Hash,并查集,^Tarjan算法^,二叉堆,^左偏树^,斜堆,^二项堆^,二叉查找树,AVL,Treap,Splay,2-d树,线段树,二维线段树,矩形树,Trie树,块状链表)
    组合数学(排列与组合,鸽笼原理,容斥原理,递推,Fibonacci数列,Catalan数列,差分序列与Stirling数,二分求解线性递推方程,生成函数,置换,Polya原理)
    概率论(简单概率,概率与平面几何,条件概率,Bayes定理,期望值)
    动态规划(最优二叉查找树,树型动规,多叉转二叉,状态压缩类动规,四边形不等式)
    字符串处理(KMP,后缀树,有限状态自动机,Huffman编码,简单密码学)
    博奕论(Nim取子游戏,博弈树,Shannon开关游戏)
    搜索(A*,ID,IDA*,随机调整,遗传算法)
    微积分初步(极限思想,导数,积分,定积分,立体解析几何)
    综合训练与题目讨论
    集训

     

    字符串的一种HASH

    据说是一个字符串HASH很好的方法。
    function ELFHash(var s:String):Integer;  
      var  
          g,h,i:LongWord;   {32位}
      begin  
         h:=0;  
         for i:=1 to Length(s) do  
          begin  
             h:=h shl 4 + Ord(s[i]);  
             g:=h and $f0000000;   {表示16进制(二进制为11110000000000000000000000000000)}   
             if g<>0 then  
                h:=h xor (g shr 24);  
             h:=h and (not g);  
          end;  
          ELFHash:=h mod M;  
      end;  
    August 19

    线段树+离散化

    平衡树可以做的事情其实线段数也可以做。
          用线段数,查第k大、插入元素、删除元素也都是O(logn),但是由于它开的空间太大,一般情况下数组要开到2^k,刚好2^(k-1)大于最大的那个数max的时候。不过这样的话有很多的空间都浪费了,所以空间太大的话就不得不用平衡树了。
          其实在处理数据之前可以把数据离散化,先排个序,用一个数组记录它们是第几大,这样就把非常大的数压缩得很小,线段数中叶子节点记录的就是压缩后的数据而不是原数,这样的化2^k只跟n有关,而和那个max无关。当然,如果是处理过程和数据的插入是一起进行的话,就先把处理过程记录下来。所以,这种算法的缺点是不能进行在线算法。不过线段数的代码比所有的平衡树都要简洁些(至少我是这么觉得的),所以线段数+离散化还是比平衡树更科学。
     

    n个数最大公约数和最小公倍数

    var
        n,i:longint;
        a:array[1..100]of longint;

    function gcd(a,b:longint):longint;{两个数的最大公约数}
    begin
        if b=0 then
            exit(a)
        else exit(gcd(b,a mod b));
    end;

    function gcdn(n:longint):longint;{n个数的最大公约数}
    begin
        if n=1 then
            exit(a[1])
        else exit(gcd(a[n],gcdn(n-1)));
    end;

    function lcm(a,b:longint):longint;{两个数的最小公倍数}
    begin
        exit(a*b div gcd(a,b));
    end;

    function lcmn(n:longint):longint;{n个数的最小公倍数}
    begin
        if n=1 then
            exit(a[1])
        else exit(lcm(a[n],lcmn(n-1)));
    end;

    begin
        read(n);
        for i:=1 to n do
            read(a[i]);
        writeln('gcd=',gcdn(n));
        writeln('lcm=',lcmn(n));
    end.

    最优代价子母树的四边形优化

    var
        d,w,f:array[1..100,1..100]of longint;
        h,a:array[0..100]of longint;
        i,j,k,l,n:longint;
    begin
        readln(n);
        for i:=1 to n do
        begin
            read(a[i]);{每个元素的大小}
            h[i]:=h[i-1]+a[i];{前缀和}
            d[i,i]:=i;{只有一个元素所以决策就是本身}
            f[i,i]:=a[i];{一堆的时候总代价为本身}
        end;
        for i:=1 to n do
        for j:=i to n do
            w[i,j]:=h[j]-h[i-1];{合并代价}
        for l:=2 to n do{按长度来DP}
        for i:=1 to n-l+1 do
        begin
            j:=i+l-1;
            f[i,j]:=maxlongint;
            for k:=d[i,j-1] to d[i+1,j] do
            if k+1<=j then  {l=2时的特判 因为d[i+1,j]=j}
              if f[i,k]+f[k+1,j]+w[i,j]<f[i,j] then
              begin
                  f[i,j]:=f[i,k]+f[k+1,j]+w[i,j];
                  d[i,j]:=k;{f[i,j]的最优决策}
              end;
        end;
        writeln(f[1,n]);{合并第一个到第n个元素的最小代价}
    end.

     

    August 18

    石子合并除四边形不等式的另一个优化

        石子合并的普通算法是做n次O(n^3)的DP,当然加上四边形不等式的优化总复杂度就是O(n^3)。
        其实,没有必要做n次DP(做n次的原因是石子是围成一个圈的,要枚举哪个是开头)。为了一次DP就实现所有的石子的相邻性,我们做如下处理:
       a[1],a[2],a[3]...a[n],a[1],a[2]...a[n-1] 
    也就是:
       a[1],a[2],a[3]...a[n],a[n+1],a[n+2]...a[2n-1]
        这样的话就只用做一次DP就行了,最后答案为max{f[i,i+n-1]|1<=i<=n}。所以石子合并加上这个优化和四边形不等式的优化后就可以优化到O(n^2)了。   
     

    p1194

    一道矩阵运算的经典题目,题解转至 matrix67.com
     
    用1 x 2的多米诺骨牌填满M x N的矩形有多少种方案,M<=5,N<2^31,输出答案mod p的结果
        
        我们以M=3为例进行讲解。假设我们把这个矩形横着放在电脑屏幕上,从右往左一列一列地进行填充。其中前n-2列已经填满了,第n-1列参差不齐。现在我们要做的事情是把第n-1列也填满,将状态转移到第n列上去。由于第n-1列的状态不一样(有8种不同的状态),因此我们需要分情况进行讨论。在图中,我把转移前8种不同的状态放在左边,转移后8种不同的状态放在右边,左边的某种状态可以转移到右边的某种状态就在它们之间连一根线。注意为了保证方案不重复,状态转移时我们不允许在第n-1列竖着放一个多米诺骨牌(例如左边第2种状态不能转移到右边第4种状态),否则这将与另一种转移前的状态重复。把这8种状态的转移关系画成一个有向图,那么问题就变成了这样:从状态111出发,恰好经过n步回到这个状态有多少种方案。比如,n=2时有3种方案,111->011->111、111->110->111和111->000->111,这与用多米诺骨牌覆盖3x2矩形的方案一一对应。
    August 16

    KM

    var
        x,y:array[1..100]of boolean;
        d,lx,ly:array[1..100]of longint;
        a:array[1..100,1..100]of longint;
        i,j,k,n,m,o:longint;
    function find(t:longint):boolean;{寻找是否有增广路}
    var
        i:longint;
    begin
        x[t]:=true;{x顶点已找过,若无增广链则为待改d值的交错树}
        for i:=1 to n do
        if (not y[i])and(lx[t]+ly[i]=a[t,i]) then{lx,ly为顶标值,相加为w即为可连等边}
        begin
            y[i]:=true;{y顶点已找过}
            if (d[i]=0)or(find(d[i])) then
            begin
                d[i]:=t;
                exit(true);
            end;
        end;
        exit(false);
    end;
    begin
        readln(n);
        for i:=1 to n do
        for j:=1 to n do
        begin
            read(a[i,j]);
            if a[i,j]>lx[i] then
                lx[i]:=a[i,j];
        end;{初始化,顶标取最大}
        for k:=1 to n do
        repeat
            fillchar(x,sizeof(x),0);
            fillchar(y,sizeof(y),0);{每次repeat清空已找过的点}
            if find(k) then
                break;{若有匹配则break并找下一x点的匹配}
            o:=maxlongint;{o的初始值}
            for i:=1 to n do
            if x[i] then
            for j:=1 to n do
            if (not y[j])and(lx[i]+ly[j]-a[i,j]<o) then
                o:=lx[i]+ly[j]-a[i,j];{寻找并改o值}
            for i:=1 to n do
            begin
                if x[i] then
                    dec(lx[i],o);{x顶点减o}
                if y[i] then
                    inc(ly[i],o);{y顶点加o}
            end;
        until false;
        o:=0;
        for i:=1 to n do
            inc(o,a[d[i],i]);{所有节点的顶标之和即为答案}
        writeln(o);
    end.
     
    August 03

    07七月

    1.文科     2.佛祖     3.短信     4.作文    5.150    6.真心话     7.A选项     8.松子生日    9.唱歌    10.网络     11.拥抱   12.图与树     13.豪儿     14.梦     15.队列优化     16.海     17.科技馆     18.想念     19.见面    20.离开     21.睡不着   22.24   23.邮件     24.哥哥     25.电话     26.SPFA   27.IP卡     28.eat me    29.photos    30.主动     31.永恒

    这个月发生了很多很奇妙的事情.