6.8练习赛题解

匿名 (未验证) 提交于 2019-12-03 00:26:01

问题描述

可以用一个网络地址和一个子网掩码描述一个子网(即连续的 IP 地址范围)。
其中子网掩码包含 32 个二进制位,前 32-n 位为 1,后 n 位为 0,网络地址的前 32-n 位任意,后 n 位为 0(0<=n<=32) 。 所有前 32-n 位和网络地址相同的 IP 都属于此网络。
例如,网地址为 194.85.160.176(二进制为 11000010 01010101 10100000 10110000), 子网掩码为 255.255.255.248(二进制为 11111111 11111111 11111111 11111000),则该 子网的 IP 地址范围是 194.85.160.176~194.85.160.183。
现在输入一些网络地址,请你求出包含所有地址的最小的网络(即包含 IP 地址最少的网络)。

输入格式

第一行为一个整数 m,
接下来的 m 行,每行一个 IP 地址,可能有多个相同的地址出现。

输出格式

输出两行,表示包含所有输入的 IP 地址的最小网络,第一行是网络地址,第二行是子网掩码。

数据范围

对于 100%的数据,m<=1000

位运算题,但是题目描述很是麻人呀。。。其实将每个输入的IP地址的4个数字都存起来,而网地址的数字就是输入的IP全部&起来。而子网掩码就只需要计算所有IP地址从左至右完全相等的位数,计算出来的位数就是子网掩码左边1的个数,右边全是0。最后注意要将网地址和子网掩码&一下。

#include<iostream> #include<cstdio> using namespace std; int answang[5],nowwang[5],wang[1010][6],ansyan[5]; int read(){     int x=0;     char c=getchar();     while(c<'0'||c>'9')c=getchar();     while(c>='0'&&c<='9'){         x=(x<<1)+(x<<3)+c-'0';         c=getchar();     }     return x; } int main(){     int k,i,j,n,p,sum,now1,now2,o;     scanf("%d",&n);     for(i=1;i<=4;i++)answang[i]=(1<<8)-1;     for(i=1;i<=n;i++){         for(j=1;j<=4;j++)wang[i][j]=read();//手动输入,比较方便          for(j=1;j<=4;j++)answang[j]&=wang[i][j];//计算网地址      }     bool flag=true;     for(i=1;i<=4;i++)ansyan[i]=0;     for(i=1;i<=4;i++){         for(j=8;j>=1;j--){             p=1<<(j-1);             sum=p&wang[1][i];             for(k=2;k<=n;k++){                 o=sum^(p&wang[k][i]);                 if(o==p){                     now1=i;now2=j;flag=false;break;                 }             }             if(!flag)break;         }         if(!flag)break;     }//计算IP地址从左至右相等的位数      if(n==1)for(i=1;i<=4;i++)ansyan[i]=255;     else {         for(i=1;i<=now1-1;i++)ansyan[i]=255;         ansyan[now1]=(1<<8)-1-((1<<j)-1);     }     printf("%d.%d.%d.%d\n",ansyan[1]&answang[1],ansyan[2]&answang[2],ansyan[3]&answang[3],ansyan[4]&answang[4]);     printf("%d.%d.%d.%d",ansyan[1],ansyan[2],ansyan[3],ansyan[4]);     return 0; }

问题描述

将自然数 1 到 n 任意排列,然后在排列的每两个数之间根据他们的大小关系插入“>”和“<”。 例如:对于 1..5 的一个排列:3 2 4 1 5,可得到:3 > 2 < 4 > 1 < 5,其中有两个“>”和 2 个“<” 。 现在给出自然数 n,问在自然数 1..n 的所有排列中,有多少个排列恰好有 k 个“<”。 请你解答这个问题。

输入格式

包含多组数据。第一行一个整数 T,表示有 T 组数据。
每组数据的占一行,包含两个整数 n 和 k,它们之间用一个空格分开。

输出格式

共 T 行,每组数据输出一行,每行一个整数,表示对应输入的排列数,这个数如果很大,则需要 输出 mod 1000000007 的结果。

数据范围

对于 30%的数据:n<=10
对于 100%的数据:k

动归递推题。
状态:f[i][j]表示1~i个数中有j个小于符号的方案总数。
边界条件:按题目:i[1,1000],j[0,i1]
状态转移方程:
当我们计算1至i中有j个小于符号的状态时,我们可以考虑在i-1个数中插入i这个数。
显然,对于i-1个数中有j个小于符号的数列插入数字,我们可以在一个小于符号处插入该数,或是在最左边插入该数,即总共f[i1][j](j+1)
或者,对于i-1个数中有j-1个小于符号的数列插入数字,我们可以在一个大于符号处插入该数,或是在最右边插入该数,即总共f[i1][j1](ij)
故得出方程:f[i][j]=f[i1][j](j+1)+f[i1][j1](ij)

#include<iostream> #include<cstdio> #include<cstring> #define LL long long using namespace std; const LL maxn=1000,mod=1000000007; LL f[maxn+10][maxn+10]; int main(){     LL i,j,t,n,k;     memset(f,0,sizeof(f));     f[0][0]=1;     for(i=1;i<=maxn;i++)         for(j=0;j<=i;j++)             f[i][j]=(f[i-1][j]*(j+1)+f[i-1][j-1]*(i-j))%mod;//动归预处理      scanf("%lld",&t);     for(i=1;i<=t;i++){         scanf("%lld%lld",&n,&k);         printf("%lld\n",f[n][k]);     }     return 0; }

问题描述

树上有n只猴子。它们编号为 1 到n。1 号猴子用它的尾巴勾着树枝。剩下的猴子都被其他的猴子用手抓着。每只猴子的每只手可以抓住另一只猴子的尾巴。从0 时刻开始,每一秒都有一只猴子松开它的一只手。这会导致一些猴子掉到地上(它们在地上也能继续松开它们的手,猴子落地的时间很短可以不计)。 你的任务是: 写一个程序,从标准输入读入猴子间抓与被抓住的关系信息,和它们放开手的顺 序,对于每一只猴子算出它落地的时间,把结果输出到标准输出。

输入格式

第一行有两个正整数n和m。n是猴子的数量,m是我们观察猴子的时间(单位为秒)。
接下来n行是初始情 况的描述。第k+1 行有两个整数表示第k个猴子抓住的猴子的编号,前一个数 代表左手抓的猴子的编号,后一个数是右手抓的猴子的编号。如果读入的数为-1 则代表猴子的手是空的。

输出格式

输出n个整数,每行一个。第i行表示第i个猴子落地的时间,如果在观察结束前猴子没有落地,那么输出-1

数据范围

1≤n≤200000,1≤m≤400000

并查集,有点像模拟。定义ans[i]表示i号猴子的落下最早时间。先将末状态(即m-1时的状态)处理出来。只要他的父亲是1,则它的ans值为inf(即-1),否则为m-1。然后一步步模拟倒推,每次将该次松手的两只猴子连接,然后将被松手的猴子的ans值更新为该次松手时间。显然对于一只猴子来说,他的ans值中所有祖先的ans值中的最小,这个在getfa操作中更新即可。

#include<iostream> #include<cstdio> using namespace std; const int maxn=400010,inf=1e9; int fa[maxn],t[maxn],n,m,l[maxn],r[maxn],bian[maxn][3];bool hand[400010][3]; int getfa(int x){     int k;     if(fa[x]==x)return x;     k=getfa(fa[x]);     t[x]=min(t[fa[x]],t[x]);//该只猴子的ans值为他所有父亲的ans最小值      fa[x]=k;     return k; } void merge(int x,int y,int o){     int x1=getfa(x),y1=getfa(y);     if(x1!=y1){         if(y1==1){             fa[x1]=1;t[x1]=o;//若父亲为1则将x与1连接          }         else{             fa[y1]=x1;t[y1]=o;         }     } } int main(){     int i,j;     scanf("%d%d",&n,&m);     for(i=1;i<=n;i++){         scanf("%d%d",&l[i],&r[i]);         fa[i]=i;t[i]=inf;     }     for(i=1;i<=m;i++){         scanf("%d%d",&bian[i][1],&bian[i][2]);         hand[bian[i][1]][bian[i][2]]=true;     }     for(i=1;i<=n;i++){         if(l[i]!=-1&&!hand[i][1])merge(i,l[i],inf);         if(r[i]!=-1&&!hand[i][2])merge(i,r[i],inf);//处理出末状态      }     for(i=1;i<=n;i++)         if(getfa(i)==1)t[i]=inf;             else t[i]=m-1;//预处理答案      for(i=m;i>=1;i--){         if(bian[i][2]==1&&l[bian[i][1]]!=-1)merge(bian[i][1],l[bian[i][1]],i-1);         if(bian[i][2]==2&&r[bian[i][1]]!=-1)merge(bian[i][1],r[bian[i][1]],i-1);//模拟倒推      }     for(i=1;i<=n;i++){         j=getfa(i);//更新该点的答案          if(t[i]==inf)printf("-1\n");         else printf("%d\n",t[i]);     }     return 0; }
文章来源: 6.8练习赛题解
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!