铁大月赛1

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次,每次使用可以让一条边通过的时间缩减为原来的一半
需要注意的是:

  1. 在一条道路上最多只能使用一次魔法
  2. 使用一次魔法只在一条道路上起作用。
  3. 你不必使用完所有的魔法。

给定以上的信息,你的任务是:求出在可以使用这不超过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;
}

还有一道计算几何的题,不会。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值