您的位置:首页 > 其它

[Hash] [最短路] [Vijos P1184] CoVH之华丽的IP伪装 (ConanIP)

2016-05-07 18:29 351 查看
这是一道神奇的题

这道题的题目说明全VJ最长……

这道题最让人看不懂……

这道题也是容易在CoVH模拟赛里卡死的一个…….

所以,我(负责任地)把所有说明打上来(可能加上各种吐槽)

Background 背景

挡在面前的铜墙铁壁

只要换一种思考方式也许就能变成一扇大门

唯一看穿真相的是一个看似小孩

智慧却过于常人的

他的名字就是名侦探柯南 //一看就柯南控出的题

Description 题目说明 //废话的开始,一脸茫然的起点

2006年4月4日 8:00 P.M





在这春暖花开, 阳光明媚的日子里, 柯南在”高效信息学在线评测系统”Vijos上切题目, 他兴高采烈的将刚刚拍完的程序提交上去, 突然发现前面排起了长长的队伍, 好多人在waiting, 又过一会, Vijos崩溃.



柯南很快意识到这不是一个普通平常的, Vijos时常发生的系统不稳定现象, 而是一场有预谋的案件. 他找到Vivian Snow (16周岁).

——->

(偶偷偷的说,这像不像喝了Aptx的Vvs?^_^)

Vivian Snow抱怨说:“ 已经是第二次了, 什么世道啊, 看我牛不顺眼啊!!”

柯南(·_·)问:“那么你当时记录下访问Vijos服务器的IP地址了么?”

Vivian Snow:” 这是自然的, 我把当时的记录拿给你看.”

柯南扫了一眼, 发现当时访问Vijos的众多IP中, 有一部分IP, 重复大量的向服务器投了巨大的数据包. 他意识到, 很可能是这些包将服务器挤爆的. 这种不算高明的手法居然把Vijos弄瘫痪了, 而攻击者很显然留下了大量证据, 只要追寻这些IP就可以了.

灰原哀忽然出现了:“ 江户川柯南, 你想得还真简单呢.”

柯南:“难道…… 难道你也在切Vijos……”

小哀:“现在没必要讨论这个了, 关键是攻击者很可能没用真实IP .”

柯南:“Er……”

柯南沉默良久, 之后缓缓站起身来:”* 如果我的推理没有错的话, 我们把访问过Vijos的IP地址调查一下, 找出当时它和哪些IP联络过, 筛选出向Vijos投过包的IP. 底下只考虑向Vijos投过包的IP, 对于两个直接联络过的IP, 他们发送的所有包的大小相加, 作为联络代价. 假定两个IP如果没有直接联络, 可以通过中间IP进行联络, 联络路径代价为联络路径中各段联络代价总和. 两个IP的联络代价为各条联络路径代价中最小的那个. 找出某一个投放过包IP地址使他与其他投放过包的IP联络代价总和最大, 那么这个IP地址就是攻击者的IP了 .”//只有这段关键

小哀:“你这个小迷糊还真不赖嘛, 可是你是怎样推理出的, 还有你怎么找出其他IP是否联络过呢?”

柯南:“我也不知道, Thunder叫我这么说的, 他说不这么说这题没法出了. 至于IP之间的联络情况, Thunder说他已经放在证据里了.”

小哀:“那么, 我去调查当时访问过Vijos的IP, 筛选出有嫌疑的.”

柯南:“等等, 话是这么说, 可是数据这么大……..”

小哀:“原来你也有犯难的时候, 你难道没发现有位大牛看我们对话很久了么.”

画外音:“那么就请看题的这位大牛帮助解决柯南的难题.”

看完这里,我整个人都是茫然的……

Input 输入

第一行 一个正整数N (N≤100000)

第2~N+1行, 每行一个IP地址 和一个正的整型数表示某个IP地址投放的包的大小

注意同一IP地址可能投放多次包, 至多不超过2000个IP地址

第N+2行 一个正整数M(M≤100000)

第N+3~M+N+2行, 每行两个IP地址用空格隔开, 表示两个IP地址之间有过联络,

不会出现两个IP多次联络的情况, 但会有IP访问2~N+1行未出现过IP的情况.

Output 输出

一行



//编辑器打不出来了……

如果存在两个或以上的IP地址到其他IP的代价总和最大, 或者不能访问到其他所有投放包的IP, 那么认为攻击者无法确定, 则将攻击者的IP地址作为222.240.168.135处理//原来Vj的IP地址,不要随便Ping

这输入输出……

Sample Input 样例输入

4

222.134.7.28 7

61.74.218.22 8

60.191.255.98 4

219.153.2.168 5

5

222.134.7.28 61.74.218.22

222.134.7.28 60.191.255.98

222.134.7.28 219.153.2.168

61.74.218.22 219.153.2.168

222.134.7.28 213.87.4.23

//不要随便Ping,Ping不通的……

Sample Output 样例输出

The ONLY truth is: it is you, 60.191.255.98

Limits 限制

见题目

Time Limit : 2s & Memory Limit : 512MB

Hints 提示//其实是后记……

结局

最终, 攻击者被追查到了, 他居然是OIBH组织成员Gengar大牛.

Gengar被捕后的独白:” 曾经我沉迷于切Vijos, 但是每每我看到一个好题, 把它拍完, 提交的时候, 都看到了waiting长龙. 于是, 我下定决心, 既然Vijos不让我AC, 我就将Vijos毁灭. 后来, 我发现一个包可以引发一连串waiting, 十个包就是十连串, 一百万个包就足以使Vijos毁灭, 我的目的也就达成了. 小弟弟, 送你个包?”

可是无论如何逼供Gengar大牛对于组织内幕一概不招, 而Gengar大牛的机房也早已被组织处理. 柯南再一次和组织擦肩而过, 命运之轮继续旋转………

后记好有感觉…….

好了,开始做题

题里说啥来着……

翻译一下关键点:

如果我的推理没有错的话(你是出题人诶……), 我们把访问过Vijos的IP地址调查一下, 找出当时它和哪些IP联络过, 筛选出向Vijos投过包的IP. 底下只考虑向Vijos投过包的IP(没投过的忽略掉,投过的看成一个点), 对于两个直接联络过的IP(有边的点), 他们发送的所有包的大小相加, 作为联络代价(边为无向边,边权为连接的两个点权之和有种HLOI Magic的感觉). 假定两个IP如果没有直接联络, 可以通过中间IP进行联络(无向图), 联络路径代价为联络路径中各段联络代价总和. 两个IP的联络代价为各条联络路径代价中最小的那个(求最短路). 找出某一个投放过包IP地址使他与其他投放过包的IP联络代价总和最大(求到各个点最短路之和最大的点), 那么这个IP地址就是攻击者的IP了.

翻译完了,就是无向图,求到各个点最短路之和最大的点

哥,你是在讲故事吧……

把样例中的图放出来

//没有投包的忽略,IP地址取第一段



现在的问题就是处理各种IP了

IP地址有四段(废话),要把它变成1,2,…的点,很容易想到哈希

搞定一个字符串哈希就好了……

给大家安利一个字符串哈希讲解,传送门

又调了半天,发现字符串哈希并不好用

哈希值过大,找编号的时候麻烦,果断放弃哈希……

(其实哈希是可以的)

还能怎么搞?STL的map

map是红黑树实现的,各种操作代价都是log2n的。2s可以霍霍……

最短路当属SPFA,总共时间复杂度约为n*m,还可以,但是常数比较非常大

又输入字符串……

这次放弃了哲学的读入,直接cin

还有一个小问题,如果存在两个或以上的IP地址到其他IP的代价总和最大, 或者不能访问到其他所有投放包的IP, 那么认为攻击者无法确定怎么搞

可以先把各个点的最短路径和求出来,再排一遍序。

情况1:第一个长度>=INT_MAX,就说明图不连通(这个可以在求和时处理)

情况2:第一个长度等于第二个长度,就说明违背了只有一个最大值,果断无法确定

正常这样就可以AC,但是细心的各位如果不放心,把关于投包的数组和dist数组扩到long long,就会发现WA一个点……

(我细心点有错吗?QAQ)

看了看数据,发现数据其实是有问题的

那个WA的点爆int了,数据生成的有问题……

好吧……这就是人品

上代码

#include <map>
#include <queue>
#include <cstdio>
#include <cstring>
#include <climits>
#include <iostream>
#include <algorithm>
#define MAXN 2010
#define MAXM 80010
using namespace std;

struct node
{
int pt;
long long best_dis;
};

bool cmp(node a,node b)
{
return a.best_dis>b.best_dis;
}

int ip_num,n,m;
int tmp;
string ip[MAXN],tmpip;
int dist[MAXN];
bool vis[MAXN];
int a[MAXN];
int head[MAXN],nex[MAXM],to[MAXM],cnt = 0;
int wei[MAXM];
node best[MAXN];
map <string,int> hash_table;
queue <int> q;

int spfa(int src)
{
while (!q.empty()) q.pop();
q.push(src);
memset(vis, false, sizeof(vis));
for (int i=1;i<=n;i++)
dist[i]=INT_MAX;
vis[src]=true;dist[src]=0;
while(!q.empty())
{
int u = q.front();
q.pop(); vis[u] = false;
for(int i = head[u]; i != -1; i = nex[i])
{
int v = to[i];
if(v == 0) continue;
if(dist[v] > dist[u] + wei[i])
{
dist[v] = dist[u] + wei[i];
if(!vis[v])
{
q.push(v);
vis[v] = true;
}
}
}
}
int ans=0;
for (int i=1;i<=n;i++)
{
if (dist[i]==INT_MAX)
return INT_MAX;
else ans+=dist[i];
}
return ans;
}

void add(int u, int v, long long w)
{
nex[cnt] = head[u];
to[cnt] = v;
wei[cnt] = w;
head[u] = cnt++;
}

int main()
{
memset(head,-1,sizeof(head));
cin>>ip_num;
for (int i=1;i<=ip_num;i++)
{
cin>>tmpip>>tmp;
if (hash_table[tmpip])
a[hash_table[tmpip]]+=tmp;
else
{
n++;
hash_table[tmpip]=n;
a[hash_table[tmpip]]=tmp;
ip
=tmpip;
}
}
cin>>m;
for (int i=1;i<=m;i++)
{
string tmp1,tmp2;
cin>>tmp1>>tmp2;
if (hash_table[tmp1]&&hash_table[tmp2])
{
add(hash_table[tmp1],hash_table[tmp2],a[hash_table[tmp1]]+a[hash_table[tmp2]]);
add(hash_table[tmp2],hash_table[tmp1],a[hash_table[tmp1]]+a[hash_table[tmp2]]);
}
}
for (int i=1;i<=n;i++)
{
best[i].best_dis=spfa(i);
best[i].pt=i;
}
sort(best+1,best+n+1,cmp);
if (best[1].best_dis==INT_MAX||best[1].best_dis==best[2].best_dis)
printf("The ONLY truth is: it is you, 222.240.168.135\n");
else
{
printf("The ONLY truth is: it is you, ");
cout<<ip[best[1].pt]<<endl;
}
return 0;
}


数据已上传
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: