【AC_CWJ_LCC】WF 2011 Practice #2 (坑爹比赛……)

这场比赛太过坑爹……

E题全世界(我指的是真的全世界)只有OpenGL Y。而且是1Y。

这个题目就是个约瑟夫问题,问最后3个死的是谁……

题目限定n>=5,实际上却有n=1以及n=2的情况……纯粹坑爹……

完全不知道如何输出。

经我们开小号暴力乱搞尝试各种输出未果……

我捉了3个题目。

A:属于送分模拟题,1Y了。好像除了小号,来参加的都Y了。

C:有两个通道,中间连着一个起飞跑道。输入n,接下来n个时间单位里两个通道会先分别加Ai,Bi部飞机。然后再飞走飞走一步飞机。问这两个通道最少要开多大。

n^2 dp 但是我的dp对那种通道上没飞机的情况下会疼……T_T,于是各种修修补补,总算Y了。

D:给定一个01串,求一个长度大于等于lower_bound的区间,使得Sum(s[i]=1)/区间长度 最大。

很Orz的题目。与斜率有很大关联……我看到有O(n)的,我n lg n的就算了>_<。反正真正理解斜率优化的,YY着就出来(其实我不理解都YY出来了)。

其实C和D都是不错的题目……

后来J题据lcc说又是坑爹数据

今晚SRM  O_O 又要被虐啦~

题目:

http://acm.hust.edu.cn:8080/judge/contest/standing2.action?cid=999

 

Andrew Stankevich’s Contest #1 (ZOJ2313~ZOJ2321)

围观到叉姐和shi哥都强力推荐大妈题。于是就切了一套。

【ZOJ2313】

给定n<=10^2000,求一个最大的k,使得满足1<=k<=n/2 && ( i*k mod n 能取遍所有0~n-1的值 )

如果要取遍的话,必须(k,n)=1。

分3种情况讨论:

n%2==1    答案是n/2 . (k,2*k+1),考虑他们的gcd为g,那么必须 g | k,看另外一边,必然有2*k % g + 1 % g ==0 即 1 % g ==0……

n%2==0 && (n/2)%2==0 答案是n/2-1,首先因为k | 2k,n/2显然不可行。然后试试n/2-1,围观(k,2(k+1)),因为n/2-1是奇数,所以2不能作公因子。然后必然 g | k,于是(2%g) * ( k%g+1%g ) % g == 0,即2*(1%g) % g == 0.于是g=1.

n%2==0 && (n/2)%2==1 答案是n/2-2……模仿上面证吧

代码:http://ideone.com/W6FjE

【ZOJ2314】

给出一个裸的无源汇上下界网络流模型,让你求一个可行流。

这个太裸直接套经典模型。

代码:http://ideone.com/t3Z6A

【ZOJ2315】

有一棵树,它的根节点不能涂色,让你给他的一些结点涂色,使得满足以下条件的情况下,涂的点最多,输出点数*1000和涂色方案。

1、每个结点只能有一个儿子被涂色。

2、如果本结点涂色了,那么该节点所有儿子都不能涂色。

直接树形dp……

代码:http://ideone.com/LTwye

【ZOJ2316】

给定一个无向图G=(V,E),这个无向图对应一个N*M的矩阵A,对于矩阵中(i,j)这个格子,如果i是第j条边的其中一个端点,那么为1,否则为0。设另一M*N的矩阵B满足,B(i,j)=A(j,i)。令B * A=C,求C这个M*M的矩阵里所有元素的和是多少。

C[i,j]=Sum{ B[i,k]*A[k,j] },他们必须都为1才能算进结果,并且只会令结果+1。那么但看中间k这个点而言,对答案的贡献显然是他度数的平方……于是水之。

代码:http://ideone.com/biWcr

【ZOJ2317】

一个N*M的矩阵(N<=10^100 M<=5),里面每个格子要么涂白色要么涂黑色,假如矩阵里不存在2*2的子矩阵同色,那么是符合条件的。然后问,合法涂色方案有多少种。

状态压缩dp->矩阵乘法。

这个其实很水,但是常数很卡……一开始TLE到死去活来……

然后加了这个优化,还要注意变成unsigned int刚刚够(你们懂的):

    Mat operator * (const Mat &oth) const{

        Mat ret;

        Clear(ret.d);

        int i,j,k;

        for (i=0;i

          for (j=0;j

            for (k=0;k

              ret.d[i][j]+=d[i][k]*oth.d[k][j];

        for (i=0;i

          for (j=0;j

            ret.d[i][j]%=Mod;

        return ret;

    }

5S TLE -> 300+MS

代码:http://ideone.com/l0UAc

【ZOJ2318】

COCO船长被众岛围观,他那只船是圆的,岛也是圆的,问能不能再不上岸的情况下跑出这个群岛。

首先显然COCO的半径可以加到岛的半径上……然后……

T_T,我直接用了ZOJ3476 The wall II的方法……引射线,然后枚举起点,拆点,DFS判能否形成圈即可。

然后围观shi哥,发现他用了另外一个判多边形的方法,一次Bellman-Ford判负权即可……

代码:http://ideone.com/p0SUv

【ZOJ2319】

给定n<=100000个元素,让你求最多能共存多少个元素,两元素能共存必须满足Si

排序+LIS不用解释的……但是要输出方案。n lg n的算法大家都知道最后在那个表里的不是LIS。但是当你插入到表中的时候,在你前面的那一个必然可以作为你得到当前这个F值的Father……这个很容易YY到。然后Father link回去就可以了。

代码:http://ideone.com/boTEV

【ZOJ2320】

给定n(n<=100)个数,每个数Ai<=10^9,然后这些数的素因子只能是前T个素数(T<=100)。问有多少个非空子集,子集内的数之积为完全平方数。

完全平方数实际上就是在每个素因子上总个数为偶数……由于只有奇偶性,可以用布尔变量表示……

然后可以根据这个列方程,每个变元Xi表示第i个数取还是不取,然后有T条方程,对应前T个素数与这n个数的奇偶关联。

一般我们guass消元的时候会发现无限解和无解的情况。无解很简单。平常的无限解就是某一步找不到主元了,这一个元其实是取true和false都可以‍,而且只对前面的方程有影响,也就是将答案*2。然后一直消下去,那么计算有多少个元酱油了,答案应该就是2^cnt。但是注意到要非空子集,而显然空的时候是符合方程的,所以将答案-1即可。

代码:http://ideone.com/I5M1q

[Ural 1099 Work Scheduling]【带花树】【Edmonds’s matching algorithm】【一般图最大匹配】【例题】

【地址】http://acm.timus.ru/problem.aspx?space=1&num=1099

给定一个无向图,求最大匹配。

 

带花树……其实这个算法很容易理解,但是实现起来非常奇葩(至少对我而言)。

除了wikiamber的程序我找到的资料看着都不大靠谱

比如昨晚找到一篇鄙视带花树的论文,然后介绍了一种O(E)的一般图最大匹配……我以为找到了神论文,然后ACM_DIY众神纷纷表示这个是错的……于是神论文成为了”神论文“……

又比如围观nocow上带花树标程,一看……这显然是裸的匈牙利算法……货不对板啊

当然……如果二分图的匈牙利算法还不会请先围观求二分图最大匹配的匈牙利算法。

实际上任意图求最大匹配也是找增广路,但是由于奇环的出现,找增广路变得困难。

首先明确一点,增广路上是不能有重复出现的点的。


二分图中,匹配边可以看作是有向的,比如定义总是从X集指向Y集。假若定义了起点必须在X集中,那么增广路中出现该匹配边时,必然是按照这个方向的。所以一个点在增广路中的奇偶性是确定的。

而这个图中,从增广路3->1->4->5和2->4->1->6可以看出,对于有奇环的任意图,1和4这两个点在增广路中所在位置的奇偶性不再一定。于是我们考虑处理这些奇环。

 

定义奇环:包含2k+1个点和k条匹配边的一个环。(如果不是这样,我们找增广路不会走上去)

对于这个奇环,k条匹配覆盖了2k个点,那么显然有一个点未被覆盖。我们拿出这个点来讨论。

比如图中的1号点就是这个这个特殊的点。除了这个点以外,其它的点都被覆盖了,所以只能向外连非匹配边,而1号点可以向外连匹配边
或非匹配边。

如果1号点没有被外面的点匹配,那么无论从其它的哪个点走进来,都能以1为终点找到增广路。(要么顺时针跑到1,要么逆时针)

同理如果1号点被外面的点匹配了,那么无论从其它的哪个点走进来,都能把这个圈看成一个点,然后从1的那条匹配边穿出去。(要么顺时针,要么逆时针)

 于是这个奇环就可以看成一个点,其主要特性由1号点体现(诸如和谁匹配了之流)。

这个合成点就叫做花。这个算法的思想就是不断地把奇环合成点,直至找到增广路(合成了某朵花以后就把整朵花当成一个点)。

考虑用BFS搜索增广路。

围观wiki这个图

由于BFS的性质,我们找到奇环只能是和同层的点,或者下下一层的点。

然后奇环的关键点必然是这棵BFS树里深度最浅的点。然后考虑合成以后,花如何展开对应的路径,使得我们能够增广。

花套花这个东西想起来都纠结>_<。

amber的程序里面并没有把点真的合成,只是弄了一个表示集合的标号:Base,然后邻接矩阵就不用变来变去了。

对于花中连向父亲的是匹配边的点,他的增广路显然是直接顺着父亲走,而如果连向父亲的边是非匹配边的点,那么显然是往后走然后跑过红色的横插边,然后再向上跑回关键点。

注意到如果连向父子的边是匹配边的点原先是不需要Father这个域来描述的,直接用表示匹配的那个域就可以了。但是现在在花中,他的Father这个域就要起作用了,用来向后指向,然后绕过红色横插边然后再跑回关键点。

实在是太精妙了。

 【代码】

#include

#include

#include

#include

#include

using namespace std;

const int N=250;

int n;

int head;

int tail;

int Start;

int Finish;

int link[N];     //表示哪个点匹配了哪个点

int Father[N];   //这个就是增广路的Father……但是用起来太精髓了

int Base[N];     //该点属于哪朵花

int Q[N];

bool mark[N];

bool map[N][N];

bool InBlossom[N];

bool in_Queue[N];

 

void CreateGraph(){

    int x,y;

    scanf("%d",&n);

    while (scanf("%d%d",&x,&y)!=EOF)

      map[x][y]=map[y][x]=1;

}

 

void BlossomContract(int x,int y){

    fill(mark,mark+n+1,false);

    fill(InBlossom,InBlossom+n+1,false);

    #define pre Father[link[i]]

    int lca,i;

    for (i=x;i;i=pre) {i=Base[i]; mark[i]=true; }

    for (i=y;i;i=pre) {i=Base[i]; if (mark[i]) {lca=i; break;} }  //寻找lca之旅……一定要注意i=Base[i]

    for (i=x;Base[i]!=lca;i=pre){

        if (Base[pre]!=lca) Father[pre]=link[i]; //对于BFS树中的父边是匹配边的点,Father向后跳

        InBlossom[Base[i]]=true;

        InBlossom[Base[link[i]]]=true;

    }

    for (i=y;Base[i]!=lca;i=pre){

        if (Base[pre]!=lca) Father[pre]=link[i]; //同理

        InBlossom[Base[i]]=true;

        InBlossom[Base[link[i]]]=true;

    }

    #undef pre

    if (Base[x]!=lca) Father[x]=y;     //注意不能从lca这个奇环的关键点跳回来

    if (Base[y]!=lca) Father[y]=x;

    for (i=1;i<=n;i++)

      if (InBlossom[Base[i]]){

          Base[i]=lca;

          if (!in_Queue[i]){

              Q[++tail]=i;

              in_Queue[i]=true;     //要注意如果本来连向BFS树中父结点的边是非匹配边的点,可能是没有入队的

          }

      }

}

 

void Change(){

    int x,y,z;

    z=Finish;

    while (z){

        y=Father[z];

        x=link[y];

        link[y]=z;

        link[z]=y;

        z=x;

    }

}

 

void FindAugmentPath(){

    fill(Father,Father+n+1,0);

    fill(in_Queue,in_Queue+n+1,false);

    for (int i=1;i<=n;i++) Base[i]=i;

    head=0; tail=1;

    Q[1]=Start;

    in_Queue[Start]=1;

    while (head!=tail){

        int x=Q[++head];

        for (int y=1;y<=n;y++)

          if (map[x][y] && Base[x]!=Base[y] && link[x]!=y)   //无意义的边

            if ( Start==y || link[y] && Father[link[y]] )    //精髓地用Father表示该点是否

                BlossomContract(x,y);

            else if (!Father[y]){

                Father[y]=x;

                if (link[y]){

                    Q[++tail]=link[y];

                    in_Queue[link[y]]=true;

                }

                else{

                    Finish=y;

                    Change();

                    return;

                }

            }

    }

}

 

void Edmonds(){

    memset(link,0,sizeof(link));

    for (Start=1;Start<=n;Start++)

      if (link[Start]==0)

        FindAugmentPath();

}

 

void output(){

    fill(mark,mark+n+1,false);

    int cnt=0;

    for (int i=1;i<=n;i++)

      if (link[i]) cnt++;

    printf("%dn",cnt);

    for (int i=1;i<=n;i++)

      if (!mark[i] && link[i]){

          mark[i]=true;

          mark[link[i]]=true;

          printf("%d %dn",i,link[i]);

      }

}

 

int main(){

//    freopen("input.txt","r",stdin);

    CreateGraph();

    Edmonds();

    output();

    return 0;

}

……

就在今天早上,我的外婆走了。

已经不是第一次经历这种场面……

上一次,是我外公。

那时我还小,还在上幼儿园

一觉醒来就满屋子找不见人,于是在家看电视

然后,等母亲回来就听到了那个噩耗

……

当时我还不怎么懂这意味着什么

直到看着我外公的遗体被火化的时候

看着母亲那种悲痛的情绪,我才明白了一点。

 

这之后,外婆几年内就患了老年痴呆症

然而今天,在房间外面,通过窗户

看着她被抢救

看着各种仪器的示数不断变化

看着不断有仪器运进去

……

然后,好像是预感到什么一样

他们把我这一辈的都叫去先吃点早餐

我们先是不肯

但是他们强烈要求……

 

当我们吃完早餐的时候

得到消息

吃完早餐不是回病房,是回家。

……

那种感觉,真是说不出口

心情很沉重很沉重

什么都不太想做

人一生下来,就注定了要归去

我们所能做到的,就只有尽力不让自己后悔

 

ZOJ Monthly, February 2011 (ZOJ3467~3476) 【lack of 3472】

月赛时必须回宿舍zzzzZZZZZZ,于是当时没怎么捉。

我太蒟蒻了T_T

3472暂时还没看题目

3476被虐了一早上,现在在死皮赖脸地看shi哥的题解

【ZOJ3467】

一只3D骑士给定他跳的方法(x,y,z),问两点间点字典序最小的路径

就是双向BFS,但是那个字典序不太好搞。然后最近学了map就果断使用了

判重时就要注意不是重复就给删掉……而是先比较步数,如果步数一样,则和原来的解一起从父节点link回去比较一下是哪一个的字典序优,如果现在的比较优,那么就把map里的指针指向该结点(相当于把原来那个点删掉,当然是lazy delete)。

因为WA重写了几遍= =,已经疼了,于是直接把两边3步都搜完最后再来判……

于是得到悲壮的8700MS。

Code:https://ideone.com/fqVUE

【ZOJ3468】

送分题。直接dp或者爆搜 T_T。

Code:http://ideone.com/8VheC

【ZOJ3469】

一个一维的数轴上,一个人初始站在X处送外卖,他跑的速度为V^(-1)。然后每个客户由两个元素(x,b)来描述,分别表示其位置以及纠结指数增长率。一个人收到外卖时的纠结指数为增长率*时间。求所有客户的纠结指数之和最小能是多少。

初看题目好像是最小哈密顿回路>_<,其实不然,如果你从X向右最远跑到某个地方,中间的肯定已经顺便送过了。然后时间的增长会增加所有未收到外面的纠结指数……于是乎每一段可以看成是单独的,于是就可以dp了。当然先要把结点按x排个序。

F[i,j,0]表示最左跑到i,最右跑到j,最后停在i上时最小纠结指数之和是多少。

F[i,j,1]表示最左跑到i,最右跑到j,最后停在j上时最小纠结指数之和是多少。

令tmp=b[1]+b[2]+..+b[i-1]   +  b[j+1]+…+b[n] (这些顾客都是没送到的)

于是

F[i,j,0]=min(  F[i+1,j,0]+(tmp+b[i])*(x[i+1]-x[i])    ,     F[i+1,j,1]+(tmp+b[i])*(x[j]-x[i])  )

F[i,j,1]=min(  F[i,j-1,0]+(tmp+b[j])*(x[j]-x[i])         ,     F[i,j-1,1]+(tmp+b[i])*(x[j]-x[j-1]))

Code:http://ideone.com/IAF8Y

【ZOJ3470】

这个转圈圈很难描述>_<,反正就是找到他变化的规律以后可以得出一个等差数列,然后我二分了一下,利用等差数列求和,找出第几圈的,然后分类讨论。

看着挺复杂,没想到1Y了。

Code:http://ideone.com/mWWUc

【ZOJ3471】

有n个星球,n<=10,然后给出一个邻接矩阵,w[i,j]表示i把j撞爆以后获得w[i,j]这么多分。问最后最多能得多少分?

直接状态压缩dp。F[i](i用2进制位表示,1表示该星球已经被撞爆,0表示还在)表示i这种状态下的最高得分。转移随意。

Code:http://ideone.com/uv9qP

【ZOJ3472】

T_T还没搞

【ZOJ3473】

F[1,1,1]=1

F[i,j,k]=max(F[i’,j’,k’])+1    其中(i’|i j’|j k’|k   但是i’,j’,k’不等全部等于i,j,k)

求Sum(F[i’,j’,k’))  i’<=i && j'<=j && k'<=k

实际上这个函数就是从1,1,1这个点每次将某位乘一个数,然后变成i,j,k的最长路嘛>_<。

于是F[i,j,k]=1+i分解成素因子的个数+j分解成素因子的个数+k分解成素因子的个数。

显然他们是单独的。令d[i]表示这个数字分解成素因子的个数(可以用筛法求得),Sum[i]=d[1]+d[2]+…+d[i]

那么ans=

i*j*k(都+1的那一部分)

+Sum[i]*j*k

+Sum[j]*i*k

+Sum[k]*i*j

后面的YY一下就知道了= =

Code:http://ideone.com/wGe5T

【ZOJ3474】

dp一下以后变成这样的题意:有n个对手,你要将他们全部打败,打某个的条件是当前体力值>Limit[i],打完以后体力-Limit[i]+R[i]。

这个其实和JSOI某题本质是一个样的。这里有师父之前的证明。

首先如果打完会加血的,顺序随意,每次打能打的就行了。

打完了这些以后,下处理打完减血的。

假如给定一个成功的顺序,对于相邻的两个(i,j),我们围观限制 (S是之前打了一些累积的体力变化值)

init_energe + S > Limit[i]                             —————–1

init_energe + S – Limit[i] + R[i] > Limit[j]      ——————2

如果说(i,j)可以交换成(j,i)那么需要

init_energe + S > Limit[j]                            //由于-Limit[i]+R[i]<0,所以由上面第二条不等式自动获得

init_engerge+ S -Limit[j]+R[j]  >  Limit[i]      ——————–3

把左边移剩init_energe+S

init_energe+S > Limit[j]+Limit[i]-R[i]      ——————-2

init_energe+S > Limit[i]+Limit[j]-R[j]     ——————–3

于是令2式右边>=3式右边。(这样必然能满足条件,使得可以交换)

Limit[j]+Limit[i]-R[i] >= Limit[i]+Limit[j]-R[j]

化简后……R[j] >= R[i] 。

就是说原来的顺序,如果满足R[j]<=R[i],就可以交换,所以任何一个满足的顺序都可以调成R[i]递减的顺序。

于是贪心即可。

Code:http://ideone.com/k8cok

【ZOJ3475】

对于NOIER来说= =,这种程度的最小割大概都是秒杀类型的吧。

一看除了隔开以外没有什么其它要求了,那就是最小割了……

顺便制作了sap的模板。

Code:http://ideone.com/yq3cN

【ZOJ3476】    update@2011.2.17

围观了shi哥的解题报告以后没看懂神马意思= =,后来又问了叉姐,终于懂了。

就是说我们这个包围圈最外围必然是一条轮廓线,然后这条轮廓线实际上就是一个环。然后这个环有没有包含一个点呢?实际上就是判断点是否在多边形内的方法,向上作射线,如果穿过这个环的次数为奇数,那么就在环的包围内。

然后我们枚举环的起点,尝试跑一个环回来,但是我们需要知道我们跑了这个环以后,有哪些点被包围在里面。

我们先对每个在输入中存在的国家向上作射线(包括入侵者国家),假设,我们枚举的这条外围路径跑去和某条射线相交了,那么对应这条射线的国家的存在性就要xor一下(因为对应的奇偶性变了)。

那么设d[x][y][z]为跑到(x,y)这个格子,状态为z(z是一个2进制数,表示每个国家在不在这个包围圈里面),这时的最短路径是多少。

然后就spfa转移跑来跑去就行了……最后当然要和起点闭合才能形成合法的包围圈,最后就用d[Start_x][Start_y][z]去更新dp[z]。

现在的dp[z]表示如果要用一个包围圈,把z这个集合中的点,都包围在内,那么至少需要多少代价。

然后dp[z]还不一定是就是对应包围z个国家的最小代价。因为不一定是一个包围圈把它们包起来就行了。

还有可能外面一个大圈,中间有几只入侵者,然后再把它们围起来

所以后面还要一个枚举子集的状态压缩动态规划,来挖空这些中间部分。

T_T,看着挺复杂,其实写起来很简单。1Y了。

Code:http://ideone.com/gAPv1