版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/z591826160/article/details/83268189
B - Berkomnadzor [contest/1070]
题面
思路
考虑二进制,屏蔽只是不影响后 位的内容,每一个区间的长度都为 ,并且合并操作一定是贪心的,能把区间扩大就尽量扩大。
YY了一下使用字典树比较好,用字典树也做过很多最小重复前缀之类的东西,思路很相似,把被禁止的网络在字典树上尽量往根部移动。
(貌似见过类似的题目,忘记在哪里了 2016北京区域赛B题好像也有一个类似的 不过当时太菜 根本没思路)
代码
const int maxn = 5e6+7;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int none = -1;
const LL linf = (LL)inf << 32 | inf;
char now[maxn];
int trie[maxn][2],fa[maxn],signs[maxn],cnt=0;
vector<ll>ans;
bool ok=true;
void insert(int len,ll id,int sign){
int rt=0;
per(i,32,32-len){
int nxt=(id>>i)&1;
if(!trie[rt][nxt]){
trie[rt][nxt]=++cnt;
fa[cnt]=rt;
}
rt=trie[rt][nxt];
}
if(signs[rt]==-1*sign)
ok=false;
signs[rt]=sign;
return ;
}
void read(){
scanf("%s",now);
int sign = now[0]=='-'?-1:1;
ll a,b,c,d,x=32;
sscanf(now+1,"%lld.%lld.%lld.%lld",&a,&b,&c,&d);
int len=strlen(now);
rep(i,0,len)
if(now[i]=='/')
sscanf(now+i+1,"%lld",&x);
ll id=(a<<24)+(b<<16)+(c<<8)+d;
insert(x,id,sign);
}
void getans(int rt,int len,ll id){
if(signs[rt]==-1){
ans.pb(id);ans.pb(len);
return ;
}
if(trie[rt][0])
getans(trie[rt][0],len+1,id);
if(trie[rt][1])
getans(trie[rt][1],len+1,id|(1ll<<(31-len)));
}
int main(){
int n;
rd(n);
rep(i,0,n)
read();
rep(i,0,cnt+1){
if(signs[i]!=-1)continue;
int rt=i;
while(fa[rt]){
if(signs[fa[rt]]==1)ok=false;
rt=fa[rt];
}
}
rep(i,0,cnt+1){
if(signs[i]!=1)continue;
int rt=i;
while(fa[rt]){
if(signs[fa[rt]]==-1)ok=false;
else signs[fa[rt]]=1;
rt=fa[rt];
}
}
rep(i,0,cnt+1){
if(signs[i]!=-1)continue;
int rt=i;
while(fa[rt]&&signs[fa[rt]]!=1){
signs[rt]=0;
signs[fa[rt]]=-1;
rt=fa[rt];
}
}
if(!ok){
puts("-1");
} else {
getans(0,-1,0);
printf("%d\n",ans.size()/2);
for(int i=0;i<ans.size();i+=2){
ll now=ans[i];
debug(now);
ll d=now&((1<<8)-1);
now>>=8;
ll c=now&((1<<8)-1);
now>>=8;
ll b=now&((1<<8)-1);
now>>=8;
ll a=now&((1<<8)-1);
printf("%lld.%lld.%lld.%lld/%lld\n",a,b,c,d,ans[i+1]);
}
}
}