ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzQ5MjYy,size_16,color_FFFFFF,t_70#pic_center)
题记
这题用位运算可以很好的解决,但是注意一个坑:>>运算符的优先级比较低,如果不注意可能算的顺序跟你的思路不一致,所以没有把握就多加括号!!!多加括号!!!注释快能顶过代码行数了,就不赘述了。
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
const int Maxn=1e5+10;
typedef long long LL;
//输入
int n;
struct point{
//ip为ip地址的十进制表示 A B C D分别表示ip地址四个部分的十进制表示
LL ip,A,B,C,D;
//ip前缀的长度
int len;
};
point IP[Maxn];
bool cmp(point a,point b){
//ip地址为第一关键字,前缀长度为第二关键字
if(a.ip==b.ip)
return a.len<b.len;
else
return a.ip<b.ip;
}
//检查b的匹配集是否是a匹配集的子集
bool check(point a,point b){
//a的前缀长度比b长,那么大一定不可能包含b(前缀越短集合越大)
if(a.len>b.len)
return false;
else{
//判断b的前缀是否包含a的前缀(前len长度)
//前缀越长越具体范围越小集合越小
if(a.ip>>(32-a.len)==b.ip>>(32-a.len))
return 1;
//它们前len长度不相等,就不是一个块的
else
return 0;
}
}
//判断a与b是否同一级(前缀长度相等)并且是否能合并成更大的一块
bool check2(point a,point b){
//前缀长度都不相等肯定不是同一级
if(a.len!=b.len)
return false;
else{
//如果a和b前len-1个前缀都相等并且第len为a为0,b为1那么a和b可以合并一大块(已经排过序了所以a是0)
//一定要注意>>优先级很低,没有把握就加括号!!!
if(((a.ip>>(32-a.len+1))==(b.ip>>(32-a.len+1)))&&((a.ip>>(32-a.len))%2==0)&&((b.ip>>(32-b.len))%2==1))
return true;
else
return false;
}
}
//返回比a前缀长度少一位的更大的块(相当于两个同级块合并成更大的一块)
point merge(point a){
point ans;
ans=a;
ans.len--;
return ans;
}
int main()
{
cin>>n;
string str;
for(int i=0;i<n;i++){
cin>>str;
//num_point:点的个数 num_slash:斜杠个数(0/1)
int num_point=0,num_slash=0;
for(int j=0;j<str.size();j++){
if(str[j]=='.')
num_point++;
else if(str[j]=='/')
num_slash++;
}
//四个部分的值分别暂存到a,b,c,d
LL a=0,b=0,c=0,d=0;
//分别表示 遍历过的的点的个数 下一个点之前的部分的和 前缀长度
LL cnt=0,sum_part=0,length=0;
for(int j=0;j<str.size();j++){
if(str[j]=='/'){
cnt=-1;//后面就没有点了为了不进入下面无关的if语句从而错误更改abcd
sum_part=0;//这里清零是为了算后面的前缀长度len
continue;
}
else if(str[j]=='.'){
cnt++;
sum_part=0;//这里清零是计算下一个部分
continue;
}
//如果不是标点执行下面的语句
sum_part=sum_part*10+str[j]-'0';
if(cnt==0) a=sum_part;
else if(cnt==1) b=sum_part;
else if(cnt==2) c=sum_part;
else if(cnt==3) d=sum_part;
if(j==str.size()-1)//把'/'后面的数字计算成十进制,这个就是前缀长度
length=sum_part;
}
IP[i].A=a;IP[i].B=b;IP[i].C=c;IP[i].D=d;
IP[i].ip=d+256*c+256*256*b+256*256*256*a;
//计算前缀长度
if(num_slash)
IP[i].len=length;
else
IP[i].len=(cnt+1)*8;
}
vector<point> vec;
//进行排序
sort(IP,IP+n,cmp);
//第一类合并(a可以涵盖b)
for(int i=0;i<n;i++){
int cnt=1;//表示i现在要"吃"的前缀是i下面的第cnt个
//a一直往下"吃"直到吃不下
while(check(IP[i],IP[i+cnt]))
cnt++;
//第i个吃掉下面比他小的后只剩下第i个
vec.push_back(IP[i]);
i+=cnt-1;//别忘了for循环会自动++
}
//第二类合并
for(vector<point>::iterator it=vec.begin();it!=vec.end()-1;it++){
if(check2((*it),(*(it+1)))){
vec.erase(it+1);
(*it)=merge((*it));
//合并后要从i上面一个开始考虑,看看能不能合并出来更大的前缀
if(it!=vec.begin())
it-=2;//别忘了for循环有一个++
else
it--;
}
}
//输出
for(int i=0;i<vec.size();i++)
cout<<vec[i].A<<"."<<vec[i].B<<"."<<vec[i].C<<"."<<vec[i].D<<"/"<<vec[i].len<<endl;
return 0;
}
样例输入1
2
1
22
样例输入2
2
10/9
10.128/9
样例输入3
2
0/1
128/1