pyf的宝藏
题目描述:pyf决心找到算法之神留在这个世界上的宝藏!现在有n个房间,每个房间内有有若干把钥匙和一枚金币,钥匙只能开相对应的房间。现在,pyf有若干次可以不用钥匙就可以开锁的机会,但是我们都知道,这是有代价的,所以我们需要知道,如果pyf学长想收集到所有的金币,需要使用多少次机会。
输入:第一行一个整数 n (1 <= n <= 1000000) – 表示存钱罐的总数. 接下来每行一个整数,第 i+1行的整数代表第i个房间的钥匙放置的房间编号
输出:一个整数表示最少使用的机会数量
样例:
input:
4
2
1
2
4
output:
2
分析:每个钥匙只有一把,每个房间也只有一个,但每个房间内可以有多把钥匙;如果错看成"第i个房间放着第k个房间的钥匙",就大wa特wa了,而且算法也从并查集变成dfs。以此为戒,读题要仔细。
源代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int fa[1000010],n;
int find(int x){
return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++){
int k;
scanf("%d",&k);
int u=find(k);
fa[i]=u;
}
int cnt=0;
for(int i=1;i<=n;i++)
if(fa[i]==i) cnt++;
cout<<cnt;
return 0;
}
pyf的愿望
题目描述:pyf在找到了算法之神留在这个世界上的全部宝藏之后,决定把这笔钱用于学校的建设上,他立下了flag:我要让学校所有的宿舍都能用上Wi-Fi。现在已知铁大一共有n(1<=n<=300)个宿舍,宿舍被数字1到n标记。一个宿舍有两种办法能用上Wi-Fi,一种是从其他宿舍链接网线,一种是自己去安装网络设备,安装网络设备需要花费 w i w_i wi(1<= w i w_i wi<=100000),链接两个宿舍楼需要花费 P i j P_{ij} Pij(1<= p i j p_{ij} pij<=100000, p i j p_{ij} pij= p j i p_{ji} pji, p i i = 0 p_{ii}=0 pii=0)
输入:
第一行:一个数n
第二行到第n+1行:第i+1行含有一个数
w
i
w_i
wi
第n+2行到第2n+1行:第n+1+i行有n个被空格分开的数,第j个数代表
p
i
j
p_{ij}
pij
输出:一个单独的数代表最小代价
样例:
input:
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
output:
9
分析:设一个宿舍安装网络设备是从p点引网线到这个宿舍,有n个宿舍,那么让学校所有的宿舍都能用上Wi-Fi的最小代价肯定是用条线路连接这n+1个地方。这不就是构成一颗最小生成树了吗!
源代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
struct node{
int u,v,d;
bool operator<(node& p){
return d<p.d;
}
}e[N];
int cnt;
int n;
int fa[310];
int find(int x){
return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
e[cnt++]={0,i,x};
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x;
scanf("%d",&x);
if(i!=j) e[cnt++]={i,j,x};
}
}
sort(e,e+cnt);
for(int i=0;i<=n;i++) fa[i]=i;
int k=0,res=0;
for(int i=0;i<cnt;i++){
int u=e[i].u,v=e[i].v,d=e[i].d;
int fu=find(u),fv=find(v);
if(fu!=fv) fa[fu]=fv,k++,res+=d;
if(k==n) break;
}
cout<<res;
return 0;
}
pyf要理财
题意:把一个有n个元素的数组分成连续的k份,使每份的和的最大值最小。
分析:最大值最小?二分!
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int a[100010],n;
int m;
int ck(ll x){
int k=0;
ll p=0;
for(int i=0;i<n;i++){
if(p+a[i]<=x) p+=a[i];
else{
k++;
p=a[i];
}
}
k++;
return k;
}
int main(){
cin>>n>>m;
ll r=0,l=-1;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
r+=a[i];
if(l<a[i]) l=a[i];
}
while(l<r){
ll mid=l+r>>1;
if(ck(mid)>m) l=mid+1;
else r=mid;
}
cout<<l;
return 0;
}
pyf的树
题意:给你一颗有nn个点的树,我们定义一棵树的重要程度为这棵树所有点的深度之和。现在需要你找出一个点,当我们以这个点为根结点时,这棵树的价值最高。
输入:给出一个数字n,代表有n个点(n<=1000000),下面n−1条边.
输出:输出你所找到的点,如果具有多个解,请输出编号最小的那个.
样例:
input:
8
1 4
5 6
4 5
6 7
6 8
2 4
3 4
output:
7
分析:需要我们找出一个点,当我们以这个点为根结点时这棵树的价值最高。那么肯定要求出以所有点为根节点时树的价值。那么怎么求呢?
考虑递推,知道以一个点为根节点这棵树的价值,推以与这个点相邻的点为根节点时这棵树的价值。
f[i]代表以i号点为根节点时,它所拥有的树的价值
源代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=2000010;
int e[N],ne[N],h[N],idx;
void add(int u,int v){
e[idx]=v,ne[idx]=h[u],h[u]=idx++;
}
int n;
ll f[N],cnt[N];
bool st[N];
void dfs1(int u){
f[u]+=1;
st[u]=1;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(st[v]) continue;
dfs1(v);
f[u]+=f[v]+cnt[v]+1;
cnt[u]+=cnt[v]+1;
}
}
int maxn=0;
void dfs(int u){
st[u]=1;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(st[v]) continue;
f[v]+=f[u]-f[v]-cnt[v]-1+n-cnt[v]-1 ;
dfs(v);
}
}
int main(){
cin>>n;
memset(h,-1,sizeof h);
for(int i=0;i<n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs1(1);
memset(st,0,sizeof st);
dfs(1);
ll maxn=0,p=-1;
for(int i=1;i<=n;i++)
if(maxn<f[i]){
maxn=f[i];
p=i;
}
cout<<p;
return 0;
}
pyf会魔法
题目:pyf有一个不为人知的秘密,就是他会魔法(不许在麻瓜面前展示魔法!)
今天pyf和一个魔法少女有一场约会,但是熬夜打dota的他今早睡过了,他现在要立即赶到少女的所在地
我们可以把pyf和少女的所在地看成一张n个点m条边的无向图上的1点和n点,pyf通过第i条边需要花费
w
i
w_i
wi 的时间。
这不就是最短路问题吗?我们都知道可以用 Dijkstra、Bellman-Ford、Floyd-WarshallDijkstra、Bellman−Ford、Floyd−Warshall等算法来解决。
但是,我们说过,pyf是一个魔法少女! 他可以使用魔法,但是因为他不想暴露他魔法师的身份,他最多只能使用kk次,每次使用可以让一条边通过的时间缩减为原来的一半
需要注意的是:
- 在一条道路上最多只能使用一次魔法
- 使用一次魔法只在一条道路上起作用。
- 你不必使用完所有的魔法。
给定以上的信息,你的任务是:求出在可以使用这不超过kk次时间魔法的条件下,从城市1到城市n最少需要多长时间。
输入:
第一行包含三个整数:n、m、k。
接下来 m 行,每行包含三个整数
A
i
A_i
Ai、
B
i
B_i
Bi 、
T
i
m
e
i
Time_i
Timei ,表示存在一条
A
i
A_i
Ai 与
B
i
B_i
Bi之间的双向道路,在不使用魔法的前提下,通过它需要
T
i
m
e
i
Time_i
Timei的时间。
对于100%的数据:1 ≤ K ≤ N ≤ 50,M ≤ 1000。1≤
A
i
A_i
Ai,
B
i
B_i
Bi ≤ N,2 ≤
T
i
m
e
i
Time_i
Timei ≤ 2000
为保证答案为整数,保证所有的
T
i
m
e
i
Time_i
Timei 均为偶数。
所有数据中的无向图保证无自环、重边,且是连通的。
输出:
输出一个整数,表示从1号城市到n号城市的最小用时。
样例:
input:
4 4 1
1 2 4
4 2 6
1 3 8
3 4 8
output:
7
分析:只能想到用dis[i][k]表示从1号点到i号点还剩k次魔法的最优解,这可以bfs求。看到数据范围1≤K≤N≤50,没问题了。
源代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int M=3010,N=110;
int e[M],ne[M],d[M],h[N],idx;
void add(int u,int v,int w){
e[idx]=v;d[idx]=w;ne[idx]=h[u];h[u]=idx++;
}
int n,m,k;
int dis[N][N];
struct node{
int x,kk,w;
bool operator>(const node& p)const {
return w>p.w;
}
};
void dfs(){
priority_queue<node,vector<node>,greater<node> >heap;
dis[1][k]=0;
node x={1,k,0};
heap.push(x);
while(heap.size()){
node p=heap.top();
heap.pop();
int u=p.x,kk=p.kk;
for(int i=h[u];~i;i=ne[i]){
int v=e[i],w=d[i];
if(dis[v][kk]>dis[u][kk]+w){
dis[v][kk]=dis[u][kk]+w;
node vv={v,kk,dis[v][kk]};
heap.push(vv);
}
if(kk>0&&dis[v][kk-1]>dis[u][kk]+w/2){
dis[v][kk-1]=dis[u][kk]+w/2;
node vv={v,kk-1,dis[v][kk-1]};
heap.push(vv);
}
}
}
}
int main(){
cin>>n>>m>>k;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w),add(v,u,w);
}
memset(dis,0x3f,sizeof dis);
dfs();
int res=0x3f3f3f3f;
for(int i=0;i<=k;i++) res=min(res,dis[n][i]);
cout<<res;
return 0;
}
pyf爱计数
题意:一个有n个元素的集合有 2 n 2^n 2n个不同子集(包含空集),现在要在这 2 n 2^n 2n个集合中取出若干集合(至少一个),使得它们的交集的元素个数为k,求取法的方案数,答案模1000000007。
输入:一行两个整数n,k。对于100%的数据,1≤n≤1000000;0≤k≤n;
输出:一行答案。
样例:
input:
1314 521
output:
390511612
分析:
源代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1000010,mod=1e9+7;
ll fac[N],inv[N];
int main(){
int n,k;
cin>>n>>k;
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++){
fac[i]=fac[i-1]*i%mod;
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
for(int i=2;i<=n;i++) inv[i]=inv[i]*inv[i-1]%mod;
ll res=0,t=2;
for(int i=n;i>=k;i--){
ll p=fac[i]*inv[i-k]%mod*inv[k]%mod*fac[n]%mod*inv[n-i]%mod*inv[i]%mod*(t-1)%mod;
if(i-k&1) res-=p;
else res+=p;
res%=mod;
t=t*t%mod;
}
cout<<(res+mod)%mod;
return 0;
}
pyf的任务
题意:在得到了算法之神的宝藏之后,pyf并不开心,因为他还有一个任务要去完成。
给定一个长度为nn序列,编号从11到nn。要求支持下面几种操作:
1.给一个区间[l,r]加上一个数xx
2.把一个区间[l,r]里小于xx的数变成xx
3.把一个区间[l,r]里大于xx的数变成xx
4.求区间[l,r]的和
5.求区间[l,r]的最大值
6.求区间[l,r]的最小值
输入描述:第一行一个整数n表示序列长度。
第二行n个整数
a
i
a_i
ai表示初始序列。
第三行一个整数m表示操作个数。
接下来m行,每行三或四个整数,第一个整数
t
p
t_p
tp 表示操作类型,接下来l,r,x或l,r表述操作数。
1<=
t
p
t_p
tp<=6,n,m<=5*
1
0
5
10^5
105,
∣
a
i
∣
|a_i|
∣ai∣<=
1
0
8
10^8
108,
t
p
t_p
tp=1时,∣x∣<=1000
t
p
t_p
tp=2或3时,∣x∣<=
1
0
8
10^8
108
输出描述:对于每个4,5,6类型的操作输出一行一个整数表示答案。
样例:
input:
2
1 2
2
2 1 2 2
4 1 2
output:
4
分析:带有区间查询、修改最大值和最小值和区间求和功能的线段树,打了至少一百五十行的代码,还有错,待修改。。。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=500010;
ll a[N],tree[N<<2],tag[N<<2],ma[N<<2],mi[N<<2];
int n;
void build(int p,int l,int r){
if(l==r){
tree[p]=a[l];
ma[p]=a[l];
mi[p]=a[l];
return;
}
int mid=l+r>>1,lc=p<<1,rc=lc|1;
build(lc,l,mid);
build(rc,mid+1,r);
tree[p]=tree[lc]+tree[rc];
ma[p]=max(ma[lc],ma[rc]);
mi[p]=min(mi[lc],mi[rc]);
}
void push_down(int p,int l,int r,ll k){
tree[p]+=k*(r-l+1);
tag[p]+=k;
ma[p]+=k;
mi[p]+=k;
}
void update(int p,int l,int r,int x,int y,ll k){
if(l>=x&&r<=y){
tree[p]+=k*(r-l+1);
tag[p]+=k;
ma[p]+=k;
mi[p]+=k;
return;
}
int mid=l+r>>1,lc=p<<1,rc=lc|1;
push_down(lc,l,mid,tag[p]);
push_down(rc,mid+1,r,tag[p]);
tag[p]=0;
if(mid>=x) update(lc,l,mid,x,y,k);
if(mid<y) update(rc,mid+1,r,x,y,k);
tree[p]=tree[lc]+tree[rc];
ma[p]=max(ma[lc],ma[rc]);
mi[p]=min(mi[lc],mi[rc]);
}
void upp(int p,int l,int r,ll k){
if(l==r){
tree[p]=k;
mi[p]=k;
ma[p]=k;
return;
}
int mid=l+r>>1,lc=p<<1,rc=p<<1|1;
if(mi[lc]<k) upp(lc,l,mid,k);
if(mi[rc]<k) upp(rc,mid+1,r,k);
tree[p]=tree[lc]+tree[rc];
ma[p]=max(ma[lc],ma[rc]);
mi[p]=min(mi[lc],mi[rc]);
}
void up(int p,int l,int r,int x,int y,ll k){
if(l>=x&&r<=y){
if(mi[p]<k){
upp(p,l,r,k);
}
return;
}
int mid=l+r>>1,lc=p<<1,rc=lc|1;
push_down(lc,l,mid,tag[p]);
push_down(rc,mid+1,r,tag[p]);
tag[p]=0;
if(mid>=x) up(lc,l,mid,x,y,k);
if(mid<y) up(rc,mid+1,r,x,y,k);
tree[p]=tree[lc]+tree[rc];
ma[p]=max(ma[lc],ma[rc]);
mi[p]=min(mi[lc],mi[rc]);
}
void downn(int p,int l,int r,ll k){
if(l==r){
tree[p]=k;
mi[p]=k;
ma[p]=k;
return;
}
int mid=l+r>>1,lc=p<<1,rc=p<<1|1;
if(ma[lc]>k) downn(lc,l,mid,k);
if(ma[rc]>k) downn(rc,mid+1,r,k);
tree[p]=tree[lc]+tree[rc];
ma[p]=max(ma[lc],ma[rc]);
mi[p]=min(mi[lc],mi[rc]);
}
void down(int p,int l,int r,int x,int y,ll k){
if(l>=x&&r<=y){
if(ma[p]>k){
downn(p,l,r,k);
}
return;
}
int mid=l+r>>1,lc=p<<1,rc=lc|1;
push_down(lc,l,mid,tag[p]);
push_down(rc,mid+1,r,tag[p]);
tag[p]=0;
if(mid>=x) up(lc,l,mid,x,y,k);
if(mid<y) up(rc,mid+1,r,x,y,k);
tree[p]=tree[lc]+tree[rc];
ma[p]=max(ma[lc],ma[rc]);
mi[p]=min(mi[lc],mi[rc]);
}
ll query(int p,int l,int r,int x,int y){
if(l>=x&&r<=y) return tree[p];
int mid=l+r>>1,lc=p<<1,rc=lc|1;
push_down(lc,l,mid,tag[p]);
push_down(rc,mid+1,r,tag[p]);
tag[p]=0;
ll res=0;
if(mid>=x) res+=query(lc,l,mid,x,y);
if(mid<y) res+=query(rc,mid+1,r,x,y);
return res;
}
ll qma(int p,int l,int r,int x,int y){
if(l>=x&&r<=y) return ma[p];
int mid=l+r>>1,lc=p<<1,rc=lc|1;
push_down(lc,l,mid,tag[p]);
push_down(rc,mid+1,r,tag[p]);
tag[p]=0;
ll res=0;
if(mid>=x) res=max(qma(lc,l,mid,x,y),res);
if(mid<y) res=max(qma(rc,mid+1,r,x,y),res);
return res;
}
ll qmi(int p,int l,int r,int x,int y){
if(l>=x&&r<=y) return mi[p];
int mid=l+r>>1,lc=p<<1,rc=lc|1;
push_down(lc,l,mid,tag[p]);
push_down(rc,mid+1,r,tag[p]);
tag[p]=0;
ll res=0x3f3f3f3f;
if(mid>=x) res=min(qmi(lc,l,mid,x,y),res);
if(mid<y) res=min(qmi(rc,mid+1,r,x,y),res);
return res;
}
int main(){
int m;
cin>>n;
for(int i=1;i<=n;i++) scanf("%lld",a+i);
build(1,1,n);
cin>>m;
while(m--){
int c;
scanf("%d",&c);
if(c==1){
int x,y;
ll k;
scanf("%d%d%lld",&x,&y,&k);
update(1,1,n,x,y,k);
}
else if(c==2){
int x,y;
ll k;
scanf("%d%d%lld",&x,&y,&k);
up(1,1,n,x,y,k);
}
else if(c==3){
int x,y;
ll k;
scanf("%d%d%lld",&x,&y,&k);
down(1,1,n,x,y,k);
}
else if(c==4){
int x,y;
scanf("%d%d",&x,&y);
cout<<query(1,1,n,x,y)<<endl;
}
else if(c==5){
int x,y;
scanf("%d%d",&x,&y);
cout<<qma(1,1,n,x,y)<<endl;
}
else{
int x,y;
scanf("%d%d",&x,&y);
cout<<qmi(1,1,n,x,y)<<endl;
}
}
return 0;
}
还有一道计算几何的题,不会。。。