题意:
输入IPv4地址空间中的一些子网号,构成一个网络集合。
输出个数最小的一个网络集合,要求其与输入集合没有交集,且相对与IPv4地址空间全集,与输入集合互为补集。
输出集合包含的子网号,格式遵循网络规范。
解析:
这题可以用Trie树来搞。
每个IP地址由32位二进制组成。整个地址空间可以表现为一棵二叉树。
用Trie的节点标记每个二进制串所能抵达的终点,即子网覆盖的终点位置。
建立Trie树后,DFS遍历树上的分支,输出没有被覆盖的分支即可。
my code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxnode = (int)5e5 + 10;
const int sigma_size = 2;
vector<string> ans;
struct Trie {
int ch[maxnode][2];
bool val[maxnode];
int sz;
void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); }
Trie() { clear(); }
int idx(char c) { return c - '0'; }
void insert(char* s, int len) {
int u = 0;
for(int i = 0; i < len; i++) {
int c = idx(s[i]);
if(!ch[u][c]) {
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = false;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = true;
}
void setIp(ll ip, int len) {
int a[4];
char buf[30];
for(int i = 3; i >= 0; i--) {
a[i] = ip % 256;
ip >>= 8;
}
sprintf(buf, "%d.%d.%d.%d/%d", a[0], a[1], a[2], a[3], len+1);
ans.pb(buf);
}
void dfs(int u, int dep, ll cur) {
if(val[u]) return;
for(int i = 1; i >= 0; i--) {
ll ip = cur | ((ll)i << (31-dep));
if(!ch[u][i])
setIp(ip, dep);
else
dfs(ch[u][i], dep+1, ip);
}
}
} trie;
int n;
char bit[33];
void trans(char* s, ll ip) {
for(int i = 31; i >= 0; i--)
s[31-i] = ((ip >> i) & 1) + '0';
s[32] = '\0';
}
ll getIp(ll a, ll b, ll c, ll d) {
return (1LL << 24) * a + (1LL<<16) * b + (1LL<<8) * c + d;
}
int main() {
int T, cas = 1;
scanf("%d", &T);
while(T--) {
trie.clear();
ans.clear();
scanf("%d", &n);
printf("Case #%d:\n", cas++);
if(n == 0) {
puts("1\n0.0.0.0/0");
continue;
}
ll a, b, c, d, len, ip;
for(int i = 0; i < n; i++) {
scanf("%lld.%lld.%lld.%lld/%lld", &a, &b, &c, &d, &len);
ip = getIp(a, b, c, d);
trans(bit, ip);
trie.insert(bit, len);
}
trie.dfs(0, 0, 0);
int size = ans.size();
printf("%d\n", size);
for(int i = 0; i < size; i++) {
printf("%s\n", ans[i].c_str());
}
}
return 0;
}