补题速度太慢了,这样可不行啊。
代码里都有解释。
看别人代码有三种写法
set,分类,和二分。
这是个是用分类思想写的,
#include <bits/stdc++.h>
using namespace std;
/* 一开始我以为是图论的,但是他说有直接相邻的和间接相邻的,
我就有点蒙蔽了。
题意也没看完全明白,要求他的下一次hack的银行附近一定也有被hack过的。
要求他不能跳来跳去的hack,那样的话好像更难。
后来才发现是个思维题。
具体 思路就是分情况。
假设最大的点是m
1 m只有一个,所有的m-1都和m相连,为m
2 m只有一个,不是所有的m-1都和m相连, 为m+1
3 m有好多,但是有一个点连接着所有的m(第二种情况自然符合哦),(为m+1);
4 其他情况就是m+2了,(即m有好多个,并且没有被一个点都连接着)
要看清题啊,不然真的很没头绪
http://codeforces.com/contest/796/problem/C
一个狗要袭击银行。狗有一个攻击力,每个银行都有一个防卫值,当这个银行被攻击以后,与这个银行相邻的和相邻的相邻的都会加一个值。
只有当狗的攻击力大于银行才可以hack,注意狗hack银行的特点
1 每个银行只能hack一次
2 除了第一个,被hack银行要和hack过的银行相邻。
3 要能打的过。攻击力大于防卫值
原题可能不是这样描述的,不过意思一样。
开始以为是图论,真是too young too simple,没想出来
*/
int a[300006];
vector <int > G[300006];
int main()
{ int m=-0x3f3f3f3f;
int xx;
int aa,b;
scanf("%d",&xx);
for(int i=1;i<=xx;i++)
{ scanf("%d",&a[i]);
m=max(m,a[i]);
}
for(int i=1;i<=xx-1;i++)
{ scanf("%d%d",&aa,&b);
G[aa].push_back(b);
G[b].push_back(aa);
}
int sum1=0;//统计是m的数量
int summ=0;//统计是m-1的数量
for(int i=1;i<=xx;i++)
{ if(a[i]==m)
summ++;
else if(a[i]==m-1)
sum1++;
}
if(summ==1)
{ int i;
int k=0;
for( i=1;i<=xx;i++)
if(a[i]==m)
break;
for(int x=0;x<G[i].size();x++)
{ int y=G[i][x];
if(a[y]==m-1)
k++;
}
if(k==sum1)
{cout<<m<<endl;
return 0;
}
else
{ cout<<m+1<<endl;
return 0;
}
}
else
{ int kk=0;
for(int i=1;i<=xx;i++)
{ kk=0;
if(a[i]==m)
kk++;
for(int j=0;j<G[i].size();j++)
{ if(a[G[i][j]]==m)
kk++;
}
if(kk==summ)
{ cout<<m+1<<endl;
return 0;
}
}
cout<<m+2<<endl;
return 0;
}
return 0;
}
第二种方法是用二分,明显比第一种好理解,也更有可能想出来。
具体见代码
写了好久,有以下错误,局部变量与全局变量的冲突
2 应该把mid当成目标值。
#include <bits/stdc++.h>
using namespace std;
/*
这道题我终于会了,看了好久了,
关键二分模拟的是过程,和思维不一样(说实话,二分)
*/
//typedef long long LL;
const int maxn=300006;
struct Node
{ int u,v,next;
}node[maxn*2];
int a[maxn];
int t[maxn];
int m;
int len;
int head[maxn];
void add(int u,int v)
{ node[len].u=u;
node[len].v=v;
node[len].next=head[u];
head[u]=len++;
}
int gg(int x)
{ int tol=0;
for(int i=1;i<=m;i++)
{ if(a[i]>x)
return 0;
t[i]=a[i]+2;
if(t[i]>x)
tol++;//最多增长两次,而这些比他还大,
//所以要不 第一次就干掉这些大佬,或者第二次干掉。
}
if(tol==0) return true;
if(m==1) return true;
for(int i=1;i<=m;i++)
{ int li=tol;
if(t[i]>x)
li--;
for(int j=head[i];j!=-1;j=node[j].next)
{ if(node[j].v==i) continue;
if(t[node[j].v]==x+1)
li--;
}
if(li==0)
return true;
}
//上面那个for看了老子好几天,原来就是枚举的时候聪明点啊
return false;
}
/*bool gg(int x)
{
for(int i=1;i<=m;i++)
t[i] = a[i];
int cnt = 0;
for(int i=1;i<=m;i++)
{
if(a[i] > x)
return false;
t[i]+=2;
if(t[i] > x)
cnt++;
}
if(m == 1)
return true;
if(cnt == 0)
return true;
for(int i=1;i<=m;i++)
{
int tmp = cnt;
if(t[i] > x)
tmp--;
for(int j=head[i];j!=-1;j=node[j].next)
{
int xx = node[j].v;
if(xx == i)
continue;
if(t[xx] == x + 1)
tmp--;
}
if(tmp == 0)
return true;
}
return false;
}*/
int solve(int big,int sma)
{ int h=big;
int l=sma;
int mid;
int ans=l;
while(h>=l)
{ mid=(h+l)/2;
if(gg(mid))
{ ans=mid;
h=mid-1;
}
else
l=mid+1;
}
return ans;
}
/*int solve(int r,int l)
{
int ans = l;
while(l <= r)
{
int mid = (l + r) / 2;
if(gg(mid))
{
ans = mid;
r = mid - 1;
}
else
l = mid + 1;
}
return ans;
}*/
int main()
{ std::ios::sync_with_stdio(false);
cin.tie(0);
int b1,b;
int big=-1;
int sma=0x3f3f3f3f;
cin>>m;
memset(head,-1,sizeof(head));
len=0;
for(int i=1;i<=m;i++)
{cin>>a[i];
big=max(big,a[i]);
sma=min(sma,a[i]);
}
for(int x=1;x<=m-1;x++)
{ cin>>b1>>b;
add(b1,b);
add(b,b1);
}
cout<<solve(big+m,sma)<<endl;
return 0;
}
又用了一个方法,就是set,这个题也算set的初体验了。
a过之后在考虑是不是可以用数组把他换掉,后来发现不好办,因为在set里删除数是比较方便的。如果在数组里要用for来找吧。。
还是set比较方便,毕竟红黑树不是白封装的。。
有几个注意的地方,
第一个tle了,因为我弄了两个set,第二个专门用来做处理。极限数组 300000,赋值30000次。。自然tle
第二次也tle了,因为set是自动排序的(在没有重载运算符的情况下),从下到大,所以其实我直接判断最大的就可以了。
恩呢,加油。
#include <bits/stdc++.h>
/*用set写,set教学写法
set的建立有三种方法
1 通过insert函数来添加
2 通过集合的拷贝
3 通过对数组的拷贝,如果不知道大小可以定义为
sizeof(a)/sizeof(int);如果里面是int的话。
具体思路是模拟,把每一个点当做第一次
*/
const int maxn=300006;
using namespace std;
int main()
{ //std::ios::sync_with_stdio(false);
multiset<int>set1;
vector <int>G[maxn];
int a[maxn];
int n;
int b,c;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{ scanf("%d",&a[i]);
set1.insert(a[i]);
}
for(int i=1;i<n;i++)
{ scanf("%d%d",&b,&c);
G[c].push_back(b);
G[b].push_back(c);
}
int all=0x3f3f3f3f;
int sum=0;
multiset<int>::iterator it;
for(int i=1;i<=n;i++)
{ //multiset<int>set1(set2);
set1.erase(set1.find(a[i]));
sum=a[i];
for(int x=0;x<G[i].size();x++)
{ int s=G[i][x];
set1.erase(set1.find(a[s]));//find是发现一个
sum=max(sum,a[s]+1);
}
//这一点卡了,如果用set进行遍历
int k=0;
if(!set1.empty()){
it=set1.end();
sum=max(sum,*(--it)+2);//由于ms.end()指向最后一位,
//而不是最后一个元素所以--it;因为multiset默认从小到大排列,比最大的就行。
}
set1.insert(a[i]);
for(int t=0;t<G[i].size();t++)
{ set1.insert(a[G[i][t]]);
}
//cout<<sum<<endl;
all=min(sum,all);
}
printf("%d\n",all);
return 0;
}
附赠福利,这样就知道了为啥 先找到地址,再删除的
set的各成员函数列表如下:
c++ stl容器set成员函数:begin()–返回指向第一个元素的迭代器
c++ stl容器set成员函数:clear()–清除所有元素
c++ stl容器set成员函数:count()–返回某个值元素的个数
c++ stl容器set成员函数:empty()–如果集合为空,返回true
c++ stl容器set成员函数:end()–返回指向最后一个元素的迭代器
c++ stl容器set成员函数:equal_range()–返回集合中与给定值相等的上下限的两个迭代器
c++ stl容器set成员函数:erase()–删除集合中的元素//(给位置。。。)
c++ stl容器set成员函数:find()–返回一个指向被查找到元素的迭代器
c++ stl容器set成员函数:get_allocator()–返回集合的分配器
c++ stl容器set成员函数:insert()–在集合中插入元素
c++ stl容器set成员函数:lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器
c++ stl容器set成员函数:key_comp()–返回一个用于元素间值比较的函数
c++ stl容器set成员函数:max_size()–返回集合能容纳的元素的最大限值
c++ stl容器set成员函数:rbegin()–返回指向集合中最后一个元素的反向迭代器
c++ stl容器set成员函数:rend()–返回指向集合中第一个元素的反向迭代器
c++ stl容器set成员函数:size()–集合中元素的数目
c++ stl容器set成员函数:swap()–交换两个集合变量
c++ stl容器set成员函数:upper_bound()–返回大于某个值元素的迭代器
c++ stl容器set成员函数:value_comp()–返回一个用于比较元素间的值的函数