[UVA11408 Count DePrimes]【线性筛】【例题】

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=2403

一个数认定为DePrimes当且仅当该数的所有素因子之和为素数。

比如说n=p1^k1 * p2^k2 * p3^k3 那么就要求p1+p2+p3为素数。

然后给出询问a,b,问[a,b]内有多少个这个数。2<=a,b<=5000000

当然这个题目本身暴力O(n lg n)的筛预处理,顺便算出因子和就可以过。

然后记得上次看到JZP神犇blog上的有提到线性筛( O(n) ),那么考虑用到这里。

这个线性筛的基本思想是让每个数字只被它的最小素因子筛到。

下面利用本题代码写个小介绍以防忘记。

void init(){

    int i,j;

    memset(low,0,sizeof(low));    //low[i]里存的是i的最小素因子,特别地,若i是素数,那么low[i]=0

    memset(sum,0,sizeof(sum));    //储存i的素因子和

    low[0]=low[1]=1;

    for (i=2;i<=5000000;i++){

        if (!low[i]) {pl[cnt++]=i; sum[i]=i;}  //i为素数,那么加入素数表,并令它的素因子和为i

        for (j=0;j

/*

这里(!low[i] || pl[j]<=low[i])就是最精髓的地方

这样保证了每次筛的新数pl[j]*i,其最小素因子是pl[j],

所以假如x不是素数,令low为x的最小素因子,则x只能以x/low * low这种形式被筛到

*/

            low*i]=pl[j];

            sum*i]=sum[i];

            if ((low[i] && pl[j]!=low[i]) || (!low[i] && i!=pl[j])) sum*i]+=pl[j];

        }

    }

    ret[0]=ret[1]=0;

    for (i=2;i<=5000000;i++) ret[i]=ret[i-1]+(!low[sum[i]]);

}

 

int main(){

    init();

    int a,b;

    while (1){

        scanf("%d",&a);

        if (a==0) break;

        scanf("%d",&b);

        printf("%dn",ret[b]-ret[a-1]);

    }

}

有了low这个数组,分解素因子也将能在O(lg n)的时间内完成。

气死我了……SRM498

哎,前两题应当都做出来了嘛。以为这次要涨rating了。

在插件里run了一下,对了就交了……谁知道他交的是之前编译的。

然后结束以后一看……发现程序怎么都不是那个样……

于是果断爆0。

5555555555555555555555555555555

5555555555555555555555555555555555555555555555555555555

55555555555555555555555555555555555555555555555555555555555

白做了……

今晚我估计都要失眠了……

RP+=INF.

坐等变灰。

 

【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;

}