C(挑花)(dfs或bfs)
链接:https://www.nowcoder.com/acm/contest/136/C
来源:牛客网
题目描述
桃花一簇开无主,可爱深红映浅红。
——《题百叶桃花》
桃花长在桃树上,树的每个节点有一个桃花,调皮的HtBest想摘尽可能多的桃花。HtBest有一个魔法棒,摘到树上任意一条链上的所有桃花,由于HtBest法力有限,只能使用一次魔法棒,请求出Htbest最多可以摘到多少个桃花。
输入描述:
第一行有一个正整数n,表示桃树的节点个数。
接下来n-1行,第i行两个正整数ai,bi ,表示桃树上的节点ai,bi之间有一条边。
输出描述:
第一行一个整数,表示HtBest使用一次魔法棒最多可以摘到多少桃花。
示例1
输入
复制
3
1 2
2 3
输出
复制
3
示例2
输入
复制
3
1 2
1 3
输出
复制
3
示例3
输入
复制
4
1 2
2 3
3 4
输出
复制
4
备注:
对于100%的测试数据:
1 ≤ n ≤ 1000000
数据量较大,注意使用更快的输入输出方式。
PS:题意就是求树的最长直径。题解:假如你的树上最长的路径的左右端点是。a,b。那么你从任意一点u出发搜到的最远的点一定是a、b中的一点,然后在从这个最远点开始搜,就可以搜到直径的另一个端点,所以要用两遍bfs或DFS。
BFS AC代码:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=1e6+10;
const int mod=1e9+7;
const int inf=1e8;
#define me(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int head[maxn],cnt=1,n,dist[maxn],ans;
bool vis[maxn];
struct node
{
int to,next;
}mp[maxn<<1];
void add(int u,int v)
{
mp[++cnt].to=v;
mp[cnt].next=head[u];
head[u]=cnt;
}
void bfs(int x)
{
queue<int>q;
me(vis,0);dist[x]=1;
q.push(x);
while(!q.empty())
{
int temp=q.front();
q.pop();
for(int i=head[temp];i!=-1;i=mp[i].next)
if(!vis[mp[i].to])
{
dist[mp[i].to]=dist[temp]+1;
vis[mp[i].to]=1;
q.push(mp[i].to);
ans=max(ans,dist[mp[i].to]);
}
}
}
int main()
{
cin>>n;
me(head,-1),me(dist,0);
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
ans=0;
bfs(1);
int pos=1;
for(int i=1;i<=n;i++)
if(dist[i]>dist[pos])
pos=i;
bfs(pos);
cout<<ans<<endl;
return 0;
}
DFS AC代码:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=1e6+10;
const int mod=1e9+7;
const int inf=1e8;
#define me(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int head[maxn],cnt=1,dist[maxn];
bool vis[maxn];
struct node
{
int to,next;
}mp[maxn<<1];
void add(int u,int v)
{
mp[++cnt].to=v;
mp[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int x)
{
vis[x]=1;
for(int i=head[x];i!=-1;i=mp[i].next)
{
if(!vis[mp[i].to])
{
dist[mp[i].to]=dist[x]+1;
dfs(mp[i].to);
}
}
}
int main()
{
int n;cin>>n;
me(head,-1),me(dist,0);
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
me(vis,0);
dfs(1);int pos=1;
for(int i=1;i<=n;i++)
if(dist[i]>dist[pos])
pos=i;
me(vis,0);
dist[pos]=1;
dfs(pos);
int ans=0;
for(int i=1;i<=n;i++)
if(ans<dist[i])
ans=dist[i];
cout<<ans<<endl;
return 0;
}
F-发电
链接:https://www.nowcoder.com/acm/contest/136/F
来源:牛客网
题目描述
HA实验是一个生产、提炼“神力水晶”的秘密军事基地,神力水晶可以让机器的工作效率成倍提升。
HA实验基地有n台发电机,标号为1-n,每台发电机的发电效率为1。
为了满足基地的用电需求,HtBest会在某台发电机上镶嵌一个等级为i的神力水晶,该发电机的发电效率是镶嵌神力水晶之前的i倍,一个发电机可以同时镶嵌多个神力水晶。
但是神力水晶有时还有别的用处,HtBest会拆掉某台发电机之前镶嵌上的一个神力水晶(设等级为i),发电机效率降为拆掉神力水晶前的1/i。
HtBest有时想知道第l到r台发电机的总发电效率为多少。
输入描述:
第一行有2个正整数n,m,分别表示发电机数量和操作数。
接下来m行,每行有3个正整数,x, y, z。
x=1时,HtBest镶嵌为第y台发电机镶嵌了一个等级为z的神力水晶,
x=2时,HtBest为第y台发电机拆掉了一个等级为z的神力水晶,
x=3时,HtBest想知道[y,z]的发电机效率的乘积。
输出描述:
对于每个x=3的操作,输出一行,表示[y,z]的发电机的效率的乘积。
由于输出过大,你需要对输出结果模1000000007(1e9+7)。
示例1
输入
复制
4 4
1 2 3
3 1 4
2 2 3
3 1 4
输出
复制
3
1
说明
操作1之后,每台发电机效率:1 3 1 1
操作3之后,每台发电机效率:1 1 1 1
示例2
输入
复制
4 4
1 2 2
1 2 3
1 3 4
3 1 4
输出
复制
24
说明
操作1之后,每台发电机效率:1 2 1 1
操作2之后,每台发电机效率:1 6 1 1
操作3之后,每台发电机效率:1 6 4 1
备注:
对于100%的测试数据:
1 ≤ n, m ≤ 1000000
1 ≤ 神力水晶等级 ≤ 100000
数据量较大,注意使用更快的输入输出方式。
PS:裸的线段树,但是要注意,当x等于2的时候不能直接除,要用费马小定理求逆元,就因为这个错了两次,悲伤。
AC代码:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=1e6+10;
const int mod=1e9+7;
const int inf=1e8;
#define me(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
ll num[maxn<<2];
void updata(int rt)
{
num[rt]=(num[rt<<1]*num[rt<<1|1])%mod;
}
ll quick_pow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1)
res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void build(int l,int r,int rt)
{
if(l==r)
{
num[rt]=1;
return ;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
updata(rt);
}
void pushdata(int c,int flog,int x,int l,int r,int rt)
{
if(l==r)
{
if(flog)
num[rt]=(num[rt]*x)%mod;
else
num[rt]=num[rt]*quick_pow(x,mod-2)%mod;
return ;
}
int m=(l+r)>>1;
if(c<=m)
pushdata(c,flog,x,l,m,rt<<1);
else
pushdata(c,flog,x,m+1,r,rt<<1|1);
updata(rt);
}
int getma(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
return num[rt];
int m=(l+r)>>1;
ll a=1,b=1;
if(L<=m)
a=getma(L,R,l,m,rt<<1);
if(R>m)
b=getma(L,R,m+1,r,rt<<1|1);
return (a%mod*b%mod)%mod;
}
int main()
{
int n,m;
cin>>n>>m;
build(1,n,1);
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(a==1)
pushdata(b,1,c,1,n,1);
else if(a==2)
pushdata(b,0,c,1,n,1);
else
printf("%d\n",getma(b,c,1,n,1));
}
return 0;
}
G 指纹锁(STL)
链接:https://www.nowcoder.com/acm/contest/136/G
来源:牛客网
题目描述
HA实验有一套非常严密的安全保障体系,在HA实验基地的大门,有一个指纹锁。
该指纹锁的加密算法会把一个指纹转化为一个不超过1e7的数字,两个指纹数值之差越小,就说明两个指纹越相似,当两个指纹的数值差≤k时,这两个指纹的持有者会被系统判定为同一个人。
现在有3种操作,共m个,
操作1:add x,表示为指纹锁录入一个指纹,该指纹对应的数字为x,如果系统内有一个与x相差≤k的指纹,则系统会忽略这次添加操作
操作2:del x,表示删除指纹锁中的指纹x,若指纹锁中多个与x相差≤k的指纹,则全部删除,若指纹锁中没有指纹x,则可以忽略该操作,
操作3:query x,表示有一个持有指纹x的人试图打开指纹锁,你需要设计一个判断程序,返回该人是否可以打开指纹锁(只要x与存入的任何一个指纹相差≤k即可打开锁)。
初始状态,指纹锁中没有任何指纹。
输入描述:
第一行有2个正整数m,k。 接下来m行,每行描述一种操作:add x,del x或query x。
输出描述:
对于每个query操作,输出一行,包含一个单词“Yes”或“No”,表示该人是否可以打开指纹锁。
示例1
输入
复制
4 3 add 1 add 10 query 5 query 4
输出
复制
No Yes
示例2
输入
复制
4 3 add 1 query 4 del 1 query 4
输出
复制
Yes No
示例3
输入
复制
6 3 add 10 query 10 add 5 query 5 del 7 //系统将指纹10和指纹5全部删除 query 8
输出
复制
Yes Yes No
备注:
对于100%的测试数据: 1 ≤ k,m ≤ 1000000 数据量较大,注意使用更快的输入输出方式。
PS:主要考set的用法
AC代码:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=1e2+10;
const int mod=1e9+7;
const int inf=1e8;
#define me(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int main()
{
int m,k;
scanf("%d%d",&m,&k);
set<int>q;
q.insert(inf);///避免set里面没有数的时候迭代器返回末尾位置
while(m--)
{
int x;char s[10];
scanf("%s %d",s,&x);
set<int>::iterator it=q.lower_bound(x-k);
if(s[0]=='a'&&(*it)>x+k)///如果返回的位置的值大于x+k说没有abs(*it-x)<=k
q.insert(x);
else if(s[0]=='d')
{
while(*it<=x+k)
{
q.erase(it);
it=q.lower_bound(x-k);
}
}
else if(s[0]=='q')
{
if(*it>x+k)///如果返回的位置的值大于x+k说没有abs(*it-x)<=k
printf("No\n");
else
printf("Yes\n");
}
}
return 0;
}
H 挖沟(最小生成树)
链接:https://www.nowcoder.com/acm/contest/136/H
来源:牛客网
题目描述
胡队长带领HA实验的战士们玩真人CS,真人CS的地图由一些据点组成,现在胡队长已经占领了n个据点,为了方便,将他们编号为1-n,为了隐蔽,胡队长命令战士们在每个据点出挖一个坑,让战士们躲在坑里。由于需要在任意两个点之间传递信息,两个坑之间必须挖出至少一条通路,而挖沟是一件很麻烦的差事,所以胡队长希望挖出数量尽可能少的沟,使得任意两个据点之间有至少一条通路,顺便,尽可能的∑d[i][j]使最小(其中d[i][j]为据点i到j的距离)。
输入描述:
第一行有2个正整数n,m,m表示可供挖的沟数。
接下来m行,每行3个数a,b,v,每行描述一条可供挖的沟,该沟可以使a与b连通,长度为v。
输出描述:
输出一行,一个正整数,表示要使得任意两个据点之间有一条通路,至少需要挖长的沟。(数据保证有解)
示例1
输入
复制
2 2
1 2 1
1 2 3
输出
复制
1
示例2
输入
复制
3 3
1 2 3
2 3 4
1 3 5
输出
复制
7
备注:
对于100%的测试数据:
1 ≤ n ≤ 100000
1 ≤ m ≤ 500000
1 ≤ v ≤ 10000
AC代码:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=5e5+10;
const int mod=1e9+7;
const int inf=1e8+10;
#define me(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int n,m,root[maxn];
struct node
{
int x,y,len;
bool friend operator<(node a,node b)
{
return a.len<b.len;
}
}a[maxn];
void inct()
{
for(int i=1;i<=n;i++)
root[i]=i;
}
int find(int x)
{
if(x==root[x])
return x;
return root[x]=find(root[x]);
}
void unite(int x,int y)
{
int x1=find(x);
int y1=find(y);
if(x1!=y1)
root[x1]=y1;
}
int kruskal()
{
int res=0;
for(int i=0;i<m;i++)
{
if(find(a[i].x)!=find(a[i].y))
{
unite(a[i].x,a[i].y);
res+=a[i].len;
}
}
return res;
}
int main()
{
cin>>n>>m;
inct();
for(int i=0;i<m;i++)
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].len);
sort(a,a+m);
cout<<kruskal()<<endl;
return 0;
}