重走长征路---OI每周刷题记录---11月23日 2013

总目录详见https://blog.csdn.net/mrcrack/article/details/84471041

做题原则,找不到测评地址的题不做。2018-11-28

重走长征路---OI每周刷题记录---11月23日  2013

本周共计16题

测评地址:

dp

1.免费午餐   //在线测评地址http://www.rqnoj.cn/problem/167

2.吞噬游戏   //在线测评地址http://www.rqnoj.cn/problem/156

3.合并傻子

4.数字游戏

5.过河

拓扑排序

6.奖金   //在线测评地址http://codevs.cn/problem/6126/

7.家谱树   //在线测评地址http://ybt.ssoier.cn:8088/problem_show.php?pid=1351

并查集

8.约会计划   //在线测评地址http://codevs.cn/problem/2639/

9.mty的考验   //在线测评地址http://www.rqnoj.cn/problem/343

10.愚蠢的宠物   //在线测评地址http://codevs.cn/problem/1503/

   17.【模板】最近公共祖先(LCA)   在线测评地址https://www.luogu.org/problemnew/show/P3379

11.亲戚  //在线测评地址http://codevs.cn/problem/1073/

高精度

12.麦森数   //在线测评地址https://www.luogu.org/problemnew/show/P1045

快速幂

13.转圈游戏   //在线测评地址https://www.luogu.org/problemnew/show/P1965

kruskal

14.最短网络   //在线测评地址http://codevs.cn/problem/1078/

15.最优布线问题    //在线测评地址http://codevs.cn/problem/1231/

单调栈

16.音乐会的等待   //在线测评地址https://www.luogu.org/problemnew/show/P1823

题解:

dp

1.免费午餐

//PID167 / 免费午餐
//在线测评地址http://www.rqnoj.cn/problem/167 
//最长下降子序列 
//因n=10^5,O(n^2)过不了,只能采用O(nlogn)的算法。 
//即动归+二分。 
//因数量最大100000,故f[0]=110000更大的值 
//样例通过,提交WA80。2019-2-6 08:55
//重新读题,发现 数量 可以 等于0
//马上 加上 0判定,即 数量 为0时,该数据被忽略
//if(a==0)continue;//马上 加上 0判定,即 数量 为0时,该数据被忽略
//提交AC。2019-2-6 09:01 数据真没想到,还有0的情况,看来是故意的。 
#include <stdio.h>
int f[100100],n,ans=0;
int main(){
    int i,a,left,right,mid;
    scanf("%d",&n);
    f[0]=110000;
    for(i=1;i<=n;i++){
        scanf("%d",&a);
        if(a==0)continue;//马上 加上 0判定,即 数量 为0时,该数据被忽略
        if(f[ans]>a)f[++ans]=a;//a比数组中的任何数都小,加入数组 
        else{
            left=0,right=ans;
            while(left+1<right){
                mid=(left+right)/2;
                if(f[mid]<=a)right=mid;
                else left=mid;
            }
            f[right]=a;//用a拔高相应位置的数。 
        }
    }
    printf("%d\n",ans);
    return 0;
}

2.吞噬游戏  

//PID156 / 吞噬比赛
//在线测评地址http://www.rqnoj.cn/problem/156 
//因N=10000,采用O(nlogn)的算法 
//动归+二分。 
//最长上升子序列 
//样例通过,提交AC。2019-2-6 17:00 
#include <stdio.h>
int n,ans=0,f[10100];
int main(){
    int i,a,left,right,mid;
    scanf("%d",&n);
    f[0]=0;
    for(i=1;i<=n;i++){
        scanf("%d",&a);
        if(f[ans]<a)f[++ans]=a;
        else{
            left=0,right=ans;
            while(left+1<right){
                mid=(left+right)/2;
                if(f[mid]>=a)right=mid;
                else left=mid;
            }
            f[right]=a;
        }
    }
    printf("%d\n",ans);
    return 0;
}

3.合并傻子

4.数字游戏

5.过河

拓扑排序

6.奖金

//6126 奖金
//在线测评地址http://codevs.cn/problem/6126/ 
//有层次关系,拓扑排序 
//钱拿最少的做root,这样容易计算增加的钱。 
//构成图。并且可能图与图之间不连通。 
//因n=10000,m=20000采用邻接表的方式存储数据 
//有一种不合理的方案
//a b
//b a
//显然,上述例子,找不到入度为0的节点,所以是不合理方案。
//n=10000,是链式数据,100+101+102+...+(100+10000-1)=(100+100+10000-1)*10000/2=5.1*10^7 int不会溢出
//有向图,工资低 指向 工资高
//用队列来处理拓扑排序 
//测试了数据 
//4 3
//1 2
//2 3
//3 4
//406
//测试了数据
//4 0
//400
//测试了数据
//10 5
//1 2
//2 3
//3 4
//5 6
//6 7
//1009
//均正确
//算法的时间复杂度O(m)
//继续测试
//2 2
//1 2
//2 1
//Poor Xed 
//提交90分,测试点1WA,数据如下
//6 5
//1 2
//2 3
//3 4
//4 5
//5 1
//Poor Xed
//仔细想想,应该是看拓扑排序后,是否还有些点未访问到,因图与图之间可能未联通。
//拓扑排序完成,应该所有点都访问过,若有未访问的点,那么,肯定就不能拓扑排序了。 
//提交AC。2019-2-6 20:10 
//想都想到了,就是有些点没串起来,归根结底,还是熟练度差了些。
//翻看了https://blog.csdn.net/mrcrack/article/details/78507306之前编写的代码 //1352 【例4-13】奖金 此次编写得棒多了。 
#include <stdio.h>
#include <string.h>
#define maxn 10100
int n,m,rd[maxn],cnt=0,head[maxn],vis[maxn],ans=0;
struct node{
    int to,next;
}e[20100];
struct node2{
    int u;
    int s;
}q[maxn];
void add_edge(int u,int v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void top_sort(){
    int i,h,t,u,v,b;
    memset(vis,0,sizeof(vis)),h=t=1;
    for(i=1;i<=n;i++)//第1轮,将入度为0的点,加入队列 
        if(rd[i]==0&&vis[i]==0){
            vis[i]=1;
            q[t].u=i,q[t].s=100,t++;
        }
    while(h<t){
        u=q[h].u;
        ans+=q[h].s;
        b=head[u];
        while(b){
            v=e[b].to;
            if(vis[v]==0){
                rd[v]--;
                if(rd[v]==0){
                    vis[v]=1;
                    q[t].u=v;
                    q[t].s=q[h].s+1;
                    t++;
                }
            }
            b=e[b].next;
        }
        h++;
    }
    for(i=1;i<=n;i++)
        if(vis[i]==0){//有部分图,找不到入度为0的节点。不能拓扑排序,这样更合理 
            ans=-1;
            break;
        }

int main(){
    int i,a,b;
    memset(rd,0,sizeof(rd)),memset(head,0,sizeof(head));
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&a,&b);
        add_edge(b,a);
        rd[a]++;
    }
    top_sort();
    if(ans==-1)printf("Poor Xed\n");
    else printf("%d\n",ans);
    return 0;

7.家谱树

//1351:【例4-12】家谱树
//在线测评地址http://ybt.ssoier.cn:8088/problem_show.php?pid=1351 
//有层次关系,很明显,拓扑排序 
//有向图,父 指向 子 
//一直样例未能通过,查了30分钟,发现 
//while(scanf("%d",&j)&&j)add_edge(i,j),rd[j]++;//此处写成while(scanf("%d",&j)&j)add_edge(i,j),rd[j]++; 
//修改,提交AC。2019-2-6 21:24
//翻看https://blog.csdn.net/mrcrack/article/details/78507306之前代码 //1351 【例4-12】家谱树
//明显现在代码写得好多了。 
#include <stdio.h>
#include <string.h>
int n,cnt=0,head[110],vis[110],q[110],rd[110];
struct node{
    int to,next;
}e[110*110];
void add_edge(int u,int v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void top_sort(){
    int u,v,b,h,t;
    memset(vis,0,sizeof(vis)),h=t=1;
    for(u=1;u<=n;u++)
        if(rd[u]==0)//找第1批入度为0的点 
            q[t]=u,vis[u]=1,t++;
    while(h<t){
        u=q[h],b=head[u],printf("%d ",u);
        while(b){
            v=e[b].to;
            if(vis[v]==0){
                rd[v]--;
                if(rd[v]==0)
                    vis[v]=1,q[t]=v,t++;
            }
            b=e[b].next;
        }
        h++;
    }
}
int main(){
    int i,j;
    memset(head,0,sizeof(head)),memset(rd,0,sizeof(rd));
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        while(scanf("%d",&j)&&j)add_edge(i,j),rd[j]++;//此处写成while(scanf("%d",&j)&j)add_edge(i,j),rd[j]++; 
    top_sort();
    return 0;
}

并查集

8.约会计划

//2639 约会计划
//在线测评地址http://codevs.cn/problem/2639/ 
//该题的问题是,查找mm,确定位置的过程,比较花时间 
//测算了一下,一对查找次数 2008*2 
//最多2008次查找,总次数2008*2*2008 
//并查集log2008,总次数2008*2*2008*log2008=1.6*10^8超时无疑 
//若采用二分+并查集 一对查找次数2*log2008
//最多2008次查找,总次数2008*2*log2008 
//并查集log2008,总次数2008*2*log2008*log2008=1.6*10^6稳过 
//试试字符串的快排,测试成功
//样例通过,提交AC。甚是高兴,越来越顺手了,这里说的是二分。2019-2-4 16:09 
#include <cstdio>
#include <cstring>
#include <algorithm>
int n,m,p,f[2020];
struct node{
    char name[20];
}q[2020];
using namespace std;
int cmp(const struct node &a,const struct node &b){
    return strcmp(a.name,b.name)<0;//自小到大排序 
}
int locate(char *name){//定位mm在数组中的位置 
    int left=1,right=n+1,mid;//right=n+1的目的,让left能取到1...n 
    while(left+1<right){
        mid=(left+right)/2;
        if(strcmp(q[mid].name,name)>0)right=mid;
        else left=mid;
    }
    return left;
}
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
void merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2)f[f2]=f1;//左靠 
}
int judge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2)return 0;//不同父亲 
    return 1;//同一父亲 
}
int main(){
    int i,j;
    char a[20],b[20];
    scanf("%d%d%d",&n,&m,&p);
    for(i=1;i<=n;i++)scanf("%s",&q[i].name);
    sort(q+1,q+1+n,cmp);//自小到大排序
    for(i=1;i<=n;i++)f[i]=i;
    while(m--){
        scanf("%s%s",a,b);
        i=locate(a),j=locate(b);
        merge(i,j);
    } 
    while(p--){
        scanf("%s%s",a,b);
        i=locate(a),j=locate(b);
        if(judge(i,j))printf("safe\n");
        else printf("cc cry\n");
    }
    return 0;
}

9.mty的考验

//PID343 / mty的考验
//在线测评地址http://www.rqnoj.cn/problem/343 
//并查集log1000=10 
//m=500,算法的时间复杂度500*10=5000 
//提交20分,测试点2-5WA,不敢相信 
//但仔细想想,在统计时,需再找一次父亲,因合并时,有些父亲还未更新
//for(i=1;i<=n;i++)cnt[getf(i)]++;//此处写成 cnt[f[i]]++; 
//修改,提交AC。2019-2-4 16:35 
#include <stdio.h>
#include <string.h>
int f[1010],n,m,cnt[1010];
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
void merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2)f[f2]=f1;
}
int main(){
    int i,u,v,ans=-1;
    memset(cnt,0,sizeof(cnt));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)f[i]=i;
    while(m--){
        scanf("%d%d",&u,&v);
        merge(u,v);
    }
    for(i=1;i<=n;i++)cnt[getf(i)]++;//此处写成 cnt[f[i]]++;
    for(i=1;i<=n;i++)
        if(ans<cnt[i])
            ans=cnt[i];
    printf("%d\n",ans);
    return 0;
}

10.愚蠢的宠物

//1503 愚蠢的宠物
//在线测评地址http://codevs.cn/problem/1503/ 
//并查集,编该题,感觉在走钢丝。 
//仔细想了想,该题只是应用了并查集的概念 
//两只宠物的速度应该一致 
//碰到有三种情况,一是其中一只已找到最后的爸爸,二是两只相遇 
//特判,有可能一开始就遇到 
//该题用的是并查集的思想,一开始很可能会反应不过来
//但冷静能解决问题。
//提交90分,测试点1WA。2019-2-4 19:22
//不可能啊,什么情况没考虑
//仔细想了想,还是while里的顺序写得不对,一开始是这样写的
//while(u!=v){
//    u=f[u],v=f[v];
//    if(f[u]==u)v=u;
//    else if(f[v]==v)u=v;
//} 
//修改好顺序,提交0分,测试点1WA。怎么可能。2019-2-4 19:27
//以下为90分代码 
//再读题,看看题解代码,该题与自己想的有出入,其实求的是最近公共祖先,如下

 

//6,8的最近公共祖先是3,
//该题应该将 最早什么时候会相遇(即步数最少) 改成 最近的相遇点(路径最近相交点) 
//https://blog.csdn.net/lyx_2016/article/details/52851842?utm_source=blogxgwz4此文 
//2、传统的求最近公共祖先的方法。求x和y到根节点的深度,先让深的跳到和浅的一层,然后他们一起向上跳,相遇的那个点就是答案
//易于理解
//1为根节点 此句很重要 
//计算深度的记忆化搜索,写得手到擒来。 
//样例通过,提交AC。2019-2-4 22:29 
//以下为AC代码。 
#include <stdio.h>
#include <string.h>
int n,f[1000100],h[1000100];
void dfs(int u){//计算深度
    if(u==1){
        h[u]=1;
        return;
    }
    if(h[u])return;//采用记忆化搜索。 
    dfs(f[u]);
    h[u]=h[f[u]]+1; 
}
int main(){
    int i,u,v;
    memset(h,0,sizeof(h));
    scanf("%d",&n);
    for(i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        f[v]=u;//此处无法算深度,必需将父子关系建立好后,才能计算深度 
    }
    for(i=1;i<=n;i++)dfs(i);//漏了此行 
    scanf("%d%d",&u,&v);
    while(h[u]>h[v])u=f[u];//深的跳到和浅的一层
    while(h[v]>h[u])v=f[v];//深的跳到和浅的一层
    while(u!=v)u=f[u],v=f[v];//此处写成 while(u!=v)u=h[u],v=h[v];
    printf("%d\n",u);
    return 0;
}

//1503 愚蠢的宠物
//在线测评地址http://codevs.cn/problem/1503/ 
//并查集,编该题,感觉在走钢丝。 
//仔细想了想,该题只是应用了并查集的概念 
//两只宠物的速度应该一致 
//碰到有三种情况,一是其中一只已找到最后的爸爸,二是两只相遇 
//特判,有可能一开始就遇到 
//该题用的是并查集的思想,一开始很可能会反应不过来
//但冷静能解决问题。
//提交90分,测试点1WA。2019-2-4 19:22
//不可能啊,什么情况没考虑
//仔细想了想,还是while里的顺序写得不对,一开始是这样写的
//while(u!=v){
//    u=f[u],v=f[v];
//    if(f[u]==u)v=u;
//    else if(f[v]==v)u=v;
//} 
//修改好顺序,提交0分,测试点1WA。怎么可能。2019-2-4 19:27
//以下为90分代码 
 
#include <stdio.h>
int n,f[1000100];
int main(){
    int i,u,v;
    scanf("%d",&n);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        f[v]=u;
    }
    scanf("%d%d",&u,&v);
    while(u!=v){
        if(f[u]==u){v=u;break;}
        else if(f[v]==v){u=v;break;}
        u=f[u],v=f[v];
    }
    printf("%d\n",u);
    return 0;
}

   17.【模板】最近公共祖先(LCA)

//P3379 【模板】最近公共祖先(LCA)
//在线测评地址https://www.luogu.org/problemnew/show/P3379 
//因 10.愚蠢的宠物   //在线测评地址http://codevs.cn/problem/1503/
//开始学习最近公共祖先(LCA),进步 需要处理 不舒服 的问题 
//算法,将查询的2个节点,先处理成离根节点的深度一致 
//计算深度时,采用记忆化搜索。 
//数据的给出,没有明确的父子关系,还是通过深搜dfs,明确父子关系 
//因N=500000,邻接矩阵已经爆内存了map[500100][500100] 
//因是树,边数N-1条,考虑储存采用无向边,边数2(N-1),邻接表没有问题
//建立树的父子关系 编好后 马上测试 没有问题 
//开始建立树的深度关系 
//N=500000,建树500000-1,建立深度关系500000log500000=500000*20=10^7勉强不超时 
//查询,M=500000,每次最多500000,总查询500000*500000=2.5*10^11超时无疑 
//M=10000,总查询M*N=10000*10000=10^8,基本超时
//故,上述算法,稳拿30分,最终分数落在30-70之间,最低30分,最高70分 
//建立树中节点,深度关系 测试,没有问题 
//样例通过,提交80分,测试点2,9TLE。怎么回事,数据太水了 
//以下为80分代码。2019-2-5 09:27 建树+确定深度关系+查询 
//预示着要学新的方法,为啥,老方法没有办法完全解决新问题。 
//突然想到,在建树的同时,能确定深度关系,马上修改代码。 
//修改代码,样例通过,提交80分,测试点2,9TLE,以下为精简后的80分代码。2019-2-5 10:07 
//https://baike.baidu.com/item/%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88/8918834?fr=aladdin此文说 
//离线算法 Tarjan
//利用并查集优越的时空复杂度,我们可以实现LCA问题的O(n+Q)算法,这里Q表示询问的次数。
//O(n+Q)太牛了,那么就Tarjan算法
//https://www.cnblogs.com/JVxie/p/4854719.html此文排版不咋滴,但值得一读,该文要是配上代码,就理想了。 
//https://blog.csdn.net/li1615882553/article/details/79762771此文,图文并茂,值得学习 
//https://blog.csdn.net/ye_xingyu/article/details/79363407看了后,发现查询的数据也可用图处理 
//样例通过,提交AC。2019-2-5 18:47 
//以下为AC代码。先学会用,再问为什么。 
#include <stdio.h>
#include <string.h>
#define maxn 500100 
struct node{
    int to;
    int next;
}e[maxn*2],ask[maxn*2];//无向边 
int N,M,S,head[maxn],cnt=0,f[maxn],vis[maxn],h_ask[maxn],cnt_ask=0,ans[maxn];
void add_edge(int u,int v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void add_ask(int u,int v){
    cnt_ask++,ask[cnt_ask].to=v,ask[cnt_ask].next=h_ask[u],h_ask[u]=cnt_ask;
}
int getf(int u){
    return f[u]==u?u:f[u]=getf(f[u]);
}
void Tarjan(int father){//树上,每个点只会被访问一次。 
    int u,v,b,f1,f2;
    u=father,vis[u]=1;
    b=head[u];
    while(b){
        v=e[b].to;
        if(vis[v]==0){//需加此句 
            Tarjan(v);
            f1=getf(u),f2=getf(v),f[f2]=f1;
        }
        b=e[b].next;
    }
    b=h_ask[u];
    while(b){
        v=ask[b].to;
        if(vis[v])ans[(b+1)/2]=getf(v);//b=1 (1+1)/2=1;b=2 (2+1)/2=1;
        b=ask[b].next;
    }
}
int main(){
    int i,u,v;
    memset(head,0,sizeof(head)),memset(vis,0,sizeof(vis)),memset(h_ask,0,sizeof(h_ask));
    scanf("%d%d%d",&N,&M,&S);
    for(i=1;i<=N;i++)f[i]=i;
    for(i=1;i<N;i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v),add_edge(v,u);//无向图 
    }
    for(i=1;i<=M;i++){//将询问加入无向图 
        scanf("%d%d",&u,&v);
        add_ask(u,v),add_ask(v,u);
    }
    Tarjan(S);
    for(i=1;i<=M;i++)printf("%d\n",ans[i]);
    return 0;
}

//P3379 【模板】最近公共祖先(LCA)
//在线测评地址https://www.luogu.org/problemnew/show/P3379 
//因 10.愚蠢的宠物   //在线测评地址http://codevs.cn/problem/1503/
//开始学习最近公共祖先(LCA),进步 需要处理 不舒服 的问题 
//算法,将查询的2个节点,先处理成离根节点的深度一致 
//计算深度时,采用记忆化搜索。 
//数据的给出,没有明确的父子关系,还是通过深搜dfs,明确父子关系 
//因N=500000,邻接矩阵已经爆内存了map[500100][500100] 
//因是树,边数N-1条,考虑储存采用无向边,边数2(N-1),邻接表没有问题
//建立树的父子关系 编好后 马上测试 没有问题 
//开始建立树的深度关系 
//N=500000,建树500000-1,建立深度关系500000log500000=500000*20=10^7勉强不超时 
//查询,M=500000,每次最多500000,总查询500000*500000=2.5*10^11超时无疑 
//M=10000,总查询M*N=10000*10000=10^8,基本超时
//故,上述算法,稳拿30分,最终分数落在30-70之间,最低30分,最高70分 
//建立树中节点,深度关系 测试,没有问题 
//样例通过,提交80分,测试点2,9TLE。怎么回事,数据太水了 
//以下为80分代码。2019-2-5 09:27 建树+确定深度关系+查询 
//预示着要学新的方法,为啥,老方法没有办法完全解决新问题。 
//突然想到,在建树的同时,能确定深度关系,马上修改代码。 
//修改代码,样例通过,提交80分,测试点2,9TLE,以下为精简后的80分代码。2019-2-5 10:07 
 
#include <stdio.h>
#include <string.h>
#define maxn 500100 
struct node{
    int to;
    int next;
}e[maxn*2];//无向边 
int N,M,S,head[maxn],cnt=0,f[maxn],vis[maxn],h[maxn];
void add_edge(int u,int v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void dfs_tree(int father){//建立树的父子关系 
    int u,v,b;
    u=father,vis[u]=1;
    b=head[u];
    while(b){
        v=e[b].to;
        if(vis[v]==0)vis[v]=1,f[v]=u,h[v]=h[u]+1,dfs_tree(v);//添加此句h[v]=h[u]+1,确定深度关系 
        b=e[b].next;
    }
}
int main(){
    int i,u,v;
    memset(head,0,sizeof(head)),memset(vis,0,sizeof(vis)),memset(h,0,sizeof(h));
    scanf("%d%d%d",&N,&M,&S);
    for(i=1;i<N;i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v),add_edge(v,u);//无向图 
    }
    f[S]=0,h[S]=1;
    dfs_tree(S);
    while(M--){//将2节点拉到同一深度,再开始向上找同一父节点 
        scanf("%d%d",&u,&v);
        while(h[u]>h[v])u=f[u];
        while(h[v]>h[u])v=f[v];
        while(u!=v)u=f[u],v=f[v];
        printf("%d\n",u);
    }
    return 0;
}

//P3379 【模板】最近公共祖先(LCA)
//在线测评地址https://www.luogu.org/problemnew/show/P3379 
//因 10.愚蠢的宠物   //在线测评地址http://codevs.cn/problem/1503/
//开始学习最近公共祖先(LCA),进步 需要处理 不舒服 的问题 
//算法,将查询的2个节点,先处理成离根节点的深度一致 
//计算深度时,采用记忆化搜索。 
//数据的给出,没有明确的父子关系,还是通过深搜dfs,明确父子关系 
//因N=500000,邻接矩阵已经爆内存了map[500100][500100] 
//因是树,边数N-1条,考虑储存采用无向边,边数2(N-1),邻接表没有问题
//建立树的父子关系 编好后 马上测试 没有问题 
//开始建立树的深度关系 
//N=500000,建树500000-1,建立深度关系500000log500000=500000*20=10^7勉强不超时 
//查询,M=500000,每次最多500000,总查询500000*500000=2.5*10^11超时无疑 
//M=10000,总查询M*N=10000*10000=10^8,基本超时
//故,上述算法,稳拿30分,最终分数落在30-70之间,最低30分,最高70分 
//建立树中节点,深度关系 测试,没有问题 
//样例通过,提交80分,测试点2,9TLE。怎么回事,数据太水了 
//以下为80分代码。2019-2-5 09:27 建树+确定深度关系+查询 
#include <stdio.h>
#include <string.h>
#define maxn 500100 
struct node{
    int to;
    int next;
}e[maxn*2];//无向边 
int N,M,S,head[maxn],cnt=0,f[maxn],vis[maxn],h[maxn];
void add_edge(int u,int v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void dfs_tree(int father){//建立树的父子关系 
    int u,v,b;
    u=father,vis[u]=1;
    b=head[u];
    while(b){
        v=e[b].to;
        if(vis[v]==0)vis[v]=1,f[v]=u,dfs_tree(v);
        b=e[b].next;
    }
}
void dfs_depth(int u){//建立树中节点,深度关系 
    if(S==u){
        h[u]=1;
        return;
    }
    if(h[u])return;//记忆化搜索 
    dfs_depth(f[u]);
    h[u]=h[f[u]]+1;
}
int main(){
    int i,u,v;
    memset(head,0,sizeof(head)),memset(vis,0,sizeof(vis)),memset(h,0,sizeof(h));
    scanf("%d%d%d",&N,&M,&S);
    for(i=1;i<N;i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v),add_edge(v,u);//无向图 
    }
    f[S]=0;
    dfs_tree(S);
    for(i=1;i<=N;i++)dfs_depth(i);
    while(M--){//将2节点拉到同一深度,再开始向上找同一父节点 
        scanf("%d%d",&u,&v);
        while(h[u]>h[v])u=f[u];
        while(h[v]>h[u])v=f[v];
        while(u!=v)u=f[u],v=f[v];
        printf("%d\n",u);
    }
    return 0;
}

11.亲戚 

//1073 家族
//在线测评地址http://codevs.cn/problem/1073/
//该题就是一个裸的并查集
//样例很快通过,提交AC. 2019-1-18
#include <stdio.h>
#define maxn 5100
int n,m,p,f[maxn];
int getf(int u){
    if(f[u]==u)return u;
    return f[u]=getf(f[u]);
}
void merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2)//左靠
        f[f2]=f1;
}
int main(){
    int i,x,y,f1,f2;
    scanf("%d%d%d",&n,&m,&p);
    for(i=1;i<=n;i++)f[i]=i;
    while(m--){
        scanf("%d%d",&x,&y);
        merge(x,y);
    }
    while(p--){
        scanf("%d%d",&x,&y);
        f1=getf(x),f2=getf(y);
        if(f1==f2)printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

高精度

12.麦森数

//P1045 麦森数

//NOIP 2003 普及组 第4题 共4题
//在线测评地址https://www.luogu.org/problemnew/show/P1045
//看完题目,发现,高精度算法是少不了了
//傻算,发现P最大3100000,结果有909526位,循环3100000*909526超时是必然的
//需加上 快速幂算法 909526*log3100000 可不超时
//同时,可用P=3021377,它有909526位,来检测程序,正确与否
//快速幂+高精度乘+高精度减
//测试了样例,有些卡顿,1s能险过,样例如下

//测试了P=3021377,1s肯定过不了。
//算了好久,1小时不到吧,答案对上了,却无法提交,因为肯定超时,P=3021377如下
 
//超时算法如下,2019-1-21
//提交,竟然测试点全MLE,#define maxn 20000000 计算 3*20000000*4/1024/1024=229M,确实MLE,一定要小心, NOIP此题要爆0了
//马上修改,#define maxn 10000000 3*10000000*4/1024/1024=115M
//提交50分,测试点1-3,9-10 TLE。
//以下为50分代码
 
//需改算法,计算最后500位
//先动手改进统计位数,2^P=10^x  x=Plog2 位数=(int)x+1 此处思路,讨论出来的,搞竞赛,最好2个人
//因x[i]*2的个位数为2,4,6,8,故x[1]-1即可。此处思路,讨论出来的,搞竞赛,最好2个人
//因输出500位,故截取1000位进行计算。
//P=1279,P=3021377,1s 通过,提交AC。2019-1-21
//以下为AC代码。
#include <stdio.h>
#include <string.h>
#include <math.h>
#define maxn 510
int P,a[maxn*2],ans[maxn*2],z[maxn*2];//此处写成 a[maxn] 程序一直输出不正确
int min(int a,int b){
    return a<b?a:b;
}
void mul(int *x,int *y){
    int i,j;
    memset(z,0,sizeof(z));
    x[0]=min(x[0],500),y[0]=min(y[0],500);
    z[0]=x[0]+y[0];
    for(i=1;i<=x[0];i++)
        for(j=1;j<=y[0];j++)
            z[i+j-1]+=x[i]*y[j];
    for(i=1;i<z[0];i++){//进位处理
        z[i+1]+=z[i]/10;
        z[i]%=10;
    }
    i=z[0];
    while(z[i]==0)i--;//去除前导0
    z[0]=i;
    memcpy(y,z,sizeof(z));
}

void quick_power(int n){
    while(n){
        if(n%2==1)mul(a,ans);
        mul(a,a);
        n/=2;
    }
}
int main(){
    int i,cnt=0;
    scanf("%d",&P);
    memset(ans,0,sizeof(ans)),ans[0]=1,ans[1]=1;
    memset(a,0,sizeof(a)),a[0]=1,a[1]=2;
    quick_power(P);
    ans[1]-=1;
    printf("%d\n",(int)(P*log10(2))+1);//打印位数
    ans[0]=min(ans[0],500);
    for(i=500;i>ans[0];i--){
            cnt++;
            printf("0");
            if(cnt%50==0)printf("\n");
    }
    for(i=ans[0];i>=1;i--){
        cnt++;
        printf("%d",ans[i]);
        if(cnt%50==0)printf("\n");
    }
    return 0;
}

//P1045 麦森数
//在线测评地址https://www.luogu.org/problemnew/show/P1045

//NOIP 2003 普及组 第4题 共4题
//看完题目,发现,高精度算法是少不了了
//傻算,发现P最大3100000,结果有909526位,循环3100000*909526超时是必然的
//需加上 快速幂算法 909526*log3100000 可不超时
//同时,可用P=3021377,它有909526位,来检测程序,正确与否
//快速幂+高精度乘+高精度减
//测试了样例,有些卡顿,1s能险过,样例如下

//测试了P=3021377,1s肯定过不了。
//算了好久,1小时不到吧,答案对上了,却无法提交,因为肯定超时,P=3021377如下
 
//超时算法如下,2019-1-21
//提交,竟然测试点全MLE,#define maxn 20000000 计算 3*20000000*4/1024/1024=229M,确实MLE,一定要小心, NOIP此题要爆0了
//马上修改,#define maxn 10000000 3*10000000*4/1024/1024=115M
//提交50分,测试点1-3,9-10 TLE。
//以下为50分代码 2019-1-21
#include <stdio.h>
#include <string.h>
#define maxn 10000000
int P,a[maxn],ans[maxn],z[maxn];
void mul(int *x,int *y){
    int i,j;
    memset(z,0,sizeof(z));
    z[0]=x[0]+y[0];
    for(i=1;i<=x[0];i++)
        for(j=1;j<=y[0];j++)
            z[i+j-1]+=x[i]*y[j];
    for(i=1;i<z[0];i++){//进位处理
        z[i+1]+=z[i]/10;
        z[i]%=10;
    }
    i=z[0];
    while(z[i]==0)i--;//去除前导0
    z[0]=i;
    memcpy(y,z,sizeof(z));
}

void quick_power(int n){
    while(n){
        if(n%2==1)mul(a,ans);
        mul(a,a);
        n/=2;
    }
}
void sub(int *x,int b){//高精度减
    int i;
    x[1]-=b;
    for(i=1;i<x[0];i++)//退位处理
        if(x[i]<0){
            x[i+1]-=1;
            x[i]+=10;
        }
    i=x[0];
    while(x[i]==0)i--;
    x[0]=i;
}
int main(){
    int i,cnt=0;
    scanf("%d",&P);
    memset(ans,0,sizeof(ans)),ans[0]=1,ans[1]=1;
    memset(a,0,sizeof(a)),a[0]=1,a[1]=2;
    quick_power(P);
    sub(ans,1);
    printf("%d\n",ans[0]);
    if(ans[0]<500){
        for(i=500;i>ans[0];i--){
            cnt++;
            printf("0");
            if(cnt%50==0)printf("\n");
        }
        for(i=ans[0];i>=1;i--){
            cnt++;
            printf("%d",ans[i]);
            if(cnt%50==0)printf("\n");
        }
    }else{
        for(i=500;i>=1;i--){
            cnt++;
            printf("%d",ans[i]);
            if(cnt%50==0)printf("\n");
        }
    }
    return 0;
}

//P1045 麦森数

//NOIP 2003 普及组 第4题 共4题
//在线测评地址https://www.luogu.org/problemnew/show/P1045
//看完题目,发现,高精度算法是少不了了
//傻算,发现P最大3100000,结果有909526位,循环3100000*909526超时是必然的
//需加上 快速幂算法 909526*log3100000 可不超时
//同时,可用P=3021377,它有909526位,来检测程序,正确与否
//快速幂+高精度乘+高精度减
//测试了样例,有些卡顿,1s能险过,样例如下

//测试了P=3021377,1s肯定过不了。
//算了好久,1小时不到吧,答案对上了,却无法提交,因为肯定超时,P=3021377如下
 


//超时算法如下,2019-1-21

//提交,竟然测试点全MLE,#define maxn 20000000 计算 3*20000000*4/1024/1024=229M,确实MLE,一定要小心, NOIP此题要爆0了

//以下为MLE算法
#include <stdio.h>
#include <string.h>
#define maxn 20000000
int P,a[maxn],ans[maxn],z[maxn];
void mul(int *x,int *y){
    int i,j;
    memset(z,0,sizeof(z));
    z[0]=x[0]+y[0];
    for(i=1;i<=x[0];i++)
        for(j=1;j<=y[0];j++)
            z[i+j-1]+=x[i]*y[j];
    for(i=1;i<z[0];i++){//进位处理
        z[i+1]+=z[i]/10;
        z[i]%=10;
    }
    i=z[0];
    while(z[i]==0)i--;//去除前导0
    z[0]=i;
    memcpy(y,z,sizeof(z));
}

void quick_power(int n){
    while(n){
        if(n%2==1)mul(a,ans);
        mul(a,a);
        n/=2;
    }
}
void sub(int *x,int b){//高精度减
    int i;
    x[1]-=b;
    for(i=1;i<x[0];i++)//退位处理
        if(x[i]<0){
            x[i+1]-=1;
            x[i]+=10;
        }
    i=x[0];
    while(x[i]==0)i--;
    x[0]=i;
}
int main(){
    int i,cnt=0;
    scanf("%d",&P);
    memset(ans,0,sizeof(ans)),ans[0]=1,ans[1]=1;
    memset(a,0,sizeof(a)),a[0]=1,a[1]=2;
    quick_power(P);
    sub(ans,1);
    printf("%d\n",ans[0]);
    if(ans[0]<500){
        for(i=500;i>ans[0];i--){
            cnt++;
            printf("0");
            if(cnt%50==0)printf("\n");
        }
        for(i=ans[0];i>=1;i--){
            cnt++;
            printf("%d",ans[i]);
            if(cnt%50==0)printf("\n");
        }
    }else{
        for(i=500;i>=1;i--){
            cnt++;
            printf("%d",ans[i]);
            if(cnt%50==0)printf("\n");
        }
    }
    return 0;
}

快速幂

13.转圈游戏

//P1965 转圈游戏
//NOIP 2013 提高组 day1 第1题  day1  共3题 
//在线测评地址https://www.luogu.org/problemnew/show/P1965 
//快速幂 
//int 容易溢出,不计算了,直接上long long 
//(10^5)^2=10^10,需要long long 
//样例通过,提交,有些紧张,还是要多测试几组数据
//提交AC。2019-2-4 22:59 
#include <stdio.h>
#define LL long long
int n,m,k,x;
LL quick_pow(int x){
    LL ans=1,a=10;
    while(x){
        if(x&1)ans=(ans*a)%n;
        a=(a*a)%n;
        x>>=1;
    }
    return ans;
}
int main(){
    scanf("%d%d%d%d",&n,&m,&k,&x);
    x=(x+(m*quick_pow(k))%n)%n;
    printf("%d\n",x);
    return 0;
}

kruskal

14.最短网络

//1078 最小生成树
//在线测评地址http://codevs.cn/problem/1078/ 
//采用Kruskal算法,理由,特别好写 快排+并查集 
//总长度最多10^5*10^2=10^7 int不会溢出 
//样例通过,提交AC。2019-2-6 16:00 
#include <cstdio>
#include <algorithm>
using namespace std;
int n,map[110][110],k=0,f[110];
struct node{
    int x,y,len;
}q[110*110/2];
int cmp(const struct node &a,const struct node &b){
    return a.len<b.len;
}
int getf(int u){
    return f[u]==u?u:f[u]=getf(f[u]);
}
int merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2){//不同爸爸 
        f[f2]=f1;
        return 1;
    }
    return 0;//同一个爸爸 
}
int main(){
    int i,j,ans=0,cnt=1;
    scanf("%d",&n);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf("%d",&map[i][j]);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            k++,q[k].x=i,q[k].y=j,q[k].len=map[i][j];
    sort(q+1,q+1+k,cmp);
    for(i=1;i<=k;i++){
        if(merge(q[i].x,q[i].y))cnt++,ans+=q[i].len;
        if(cnt==n)break;
    }
    printf("%d\n",ans);
    return 0;
}

15.最优布线问题 

//1231 最优布线问题
//在线测评地址http://codevs.cn/problem/1231/ 
//重边不要紧,一个快排之后,小的再前,大的在后 
//别忘了,还有并查集,同样可以继续消除重边的影响。 
//故,该题采用Kruskal算法,裸的。 
//最后,还是n台的最小生成树问题。 
//样例通过,提交AC。2019-2-6 16:23 
#include <cstdio>
#include <algorithm>
#define LL long long
using namespace std;
int n,m,f[100100];
LL ans=0;
struct node{
    int x,y,len;
}q[100100];
int cmp(const struct node &a,const struct node &b){
    return a.len<b.len;
}
int getf(int u){
    return f[u]==u?u:f[u]=getf(f[u]);
}
int merge(int u,int v){
    int f1=getf(u),f2=getf(v);
    if(f1!=f2){
        f[f2]=f1;
        return 1;
    }
    return 0;
}
int main(){
    int i,x,y,len,cnt=1;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&len);
        q[i].x=x,q[i].y=y,q[i].len=len;
    }
    sort(q+1,q+1+m,cmp);
    for(i=1;i<=m;i++){
        if(merge(q[i].x,q[i].y))ans+=q[i].len,cnt++;
        if(cnt==n)break;
    }
    printf("%lld\n",ans);
    return 0;
}

单调栈

16.音乐会的等待

//P1823 [COI2007] Patrik 音乐会的等待
//在线测评地址https://www.luogu.org/problemnew/show/P1823 
//因N=500000,算法的时间复杂度要么O(n),要么O(nlogn)
//单调栈 
//测试数据有点多,有23组,
//样例通过,提交0分,
//断定是int溢出
//极限情况,ans值为
//1+2+3+...+499999=(1+499999)*499999/2=1.25*10^11只能用long long
//修改,提交8分,测试点1-20,23WA.2019-2-3 22:31
//https://blog.csdn.net/qq_35786326/article/details/81734888此文代码写得不错 
//之间没有人比A或B高 此句理解了好长时间 5 4 3 2 插入3,能被插入的3看见的是 2,3,4 
//修改,提交80分,测试点21-23TLE。2019-2-3 23:07
//还算满意,以下为80分代码。
//参考了此文的写法http://hzwer.com/445.html 
//考虑了特判,栈中元素全是a,样例通过,提交AC。2019-2-3 23:47
//二分,编写时,关键要清楚自己要的数据出现在left,还是right。注意特判情况。 
#include <stdio.h>
#include <string.h>
#define LL long long
int n,stack[500100],top=0;//单调栈,自栈底到栈顶,逐渐减小 
int main(){
    int a,left,right,mid;
    LL ans=0;
    scanf("%d",&n);
    scanf("%d",&a);
    stack[++top]=a,n--;//第1个元素 
    while(n--){
        scanf("%d",&a);//此过程中,ans增加的值,是能看见a向左能看见的人数 
        if(top>0&&stack[top]>a)stack[++top]=a,ans++;
        else{
            left=0,right=top;
            while(left+1<right){
                mid=(left+right)/2;
                if(stack[mid]<=a)right=mid;
                else left=mid;
            }
            if(left==0)ans+=top-left;//特判,栈中元素全是a//此处写成 ans+=top-right+1;
            else ans+=top-left+1;
            while(top>0&&stack[top]<a)top--;
            stack[++top]=a;
        }
    }
    printf("%lld\n",ans);
    return 0;

//P1823 [COI2007] Patrik 音乐会的等待
//在线测评地址https://www.luogu.org/problemnew/show/P1823 
//因N=500000,算法的时间复杂度要么O(n),要么O(nlogn)
//单调栈 
//测试数据有点多,有23组,
//样例通过,提交0分,
//断定是int溢出
//极限情况,ans值为
//1+2+3+...+499999=(1+499999)*499999/2=1.25*10^11只能用long long
//修改,提交8分,测试点1-20,23WA.2019-2-3 22:31
//https://blog.csdn.net/qq_35786326/article/details/81734888此文代码写得不错 
//之间没有人比A或B高 此句理解了好长时间 5 4 3 2 插入3,能被插入的3看见的是 2,3,4 
//修改,提交80分,测试点21-23TLE。2019-2-3 23:07
//还算满意,以下为80分代码。 
#include <stdio.h>
#include <string.h>
#define LL long long
int n,stack[500100],top=0;//单调栈,自栈底到栈顶,逐渐减小 
int main(){
    int i,a,t;
    LL ans=0;
    scanf("%d",&n);
    while(n--){
        t=1;
        scanf("%d",&a);//此过程中,ans增加的值,是能看见a向左能看见的人数 
        while(top>0&&stack[top]<=a){
            if(stack[top]==a)t++;
            ans++;
            top--;//被a挡住的弹出栈,或是与a相等的先弹出栈 
        } 
        if(top>0)ans++;//栈顶元素大于a 
        for(i=1;i<=t;i++)stack[++top]=a;//a必需放入栈 
    }
    printf("%lld\n",ans);
    return 0;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值