acwing第8周周赛(哈希+图论

acwing第8周周赛(哈希+图论

给定一个 n 个点 m 条边的有向强连通图。

点的编号为 1∼n,边的长度均为 1。

给定一条由点 s 到点 t 的简单路径 p1,p2,…,pk,其中 p1=s,pk=t。

注意,这条路经不一定是从点 s 到点 t 的最短路径。

现在,小明要沿着这条路径从点 s 走到点 t。

在他的行进过程中,手机上的导航软件将持续为他导航,持续为他提供最短行进线路建议。

当然,他并不一定会采纳这些建议,因为他一定会沿着之前给定的线路行进。

设想一下,在行进中,导航软件的工作过程。

首先,在点 s 处,导航软件会找到并显示出一条从点 s 到点 t 的最短路径。

如果小明的行进线路恰好与软件推荐线路一致,则软件推荐线路将不会发生任何改变。

但是,如果小明在某一点处,行进线路与软件推荐线路发生了分歧,例如,软件推荐前往点 v,小明却前往了点 w。

那么,在他到达点 w 后,软件就会实时更新推荐线路,即找到并显示出一条从点 w 到点 t 的最短路径。

导航软件会一直工作到小明到达点 t 为止,在这一过程中,软件的提供线路可能会经过若干次更新。

例如,给定一个有向强连通图,如下所示:

a.png

给出的简单路径为 1,2,3,4

那么,小明从点 1 出发,导航软件找到并显示出一条从点 1 到点 4 的最短路径,这样的路径只有一条 [1,5,4]。

小明并未听从软件的建议,坚持到达了点 2,此时软件推荐线路实时更新,提供出一条点 2 到点 4 的最短路径,例如 [2,6,4](注意,软件提供的最短路径也有可能是 [2,3,4])。

小明还是不听软件的建议,坚持到达了点 3,此时软件推荐线路再次更新,提供出一条点 3 到点 4 的最短路径,即 [3,4]。

最后,小明沿软件提供路线,到达目的地点 4,软件完成导航。

总的来看,软件推荐线路发生了两次更新。

值得注意的是,如果软件在第一次更新推荐线路时,给出的最短路径为 [2,3,4],则小明将按照推荐线路走到终点,软件将无需再次更新推荐线路。

也就是说,由于软件在推荐最短路径时具有随机性,所以在整个行进过程中,软件更新推荐线路的次数并不确定。

现在,给定有向图和行进路线,请你求出软件更新推荐线路的最小可能次数和最大可能次数。

输入格式
第一行包含两个整数 n 和 m。

接下来 m 行,每行包含两个整数 u,v,表示存在一条从点 u 到点 v 的有向边。

随后一行包含一个整数 k。

最后一行包含 k 个整数 p1,p2,…,pk。

输出格式
一行,空格隔开的两个整数,表示软件更新推荐路线的最小可能次数和最大可能次数。

数据范围
前三个测试点满足 1≤n≤10。
全部测试点满足 2≤n≤m≤2×105,1≤u,v≤n,u≠v,2≤k≤n,1≤pi≤n,pi 两两不同。
保证输入没有重边,但是可能同时存在端点相同方向不同的两条边,例如 (a,b) 和 (b,a)。

输入样例1:
6 9
1 5
5 4
1 2
2 3
3 4
4 1
2 6
6 4
4 2
4
1 2 3 4
输出样例1:
1 2


1.思考我为什么要反向建图?
因为我要维护的是每次到达原点的dist【i】
而堆优化版的dj是各个点到每个给定点的最短距离
2.然后对于每次走的路径,询问dist【path[i]】是否等于dist【path[i+1]】+1如果不相等那么一定会推荐
两个都加
如果相等但是最短路只有这一条那么也一定会推荐
两个都加
如果相等但是最短路不止这一条那么不一定会推荐maxcnt++,mincnt不加

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200005;
int h[N],ne[N],e[N],idx;
typedef pair<int ,int>PII;
priority_queue<PII,vector<PII>,greater<PII> >heap;
int n,m,s,t;
int path[N],d[N],vis[N];
int cnt[N];
int ansmax,ansmin;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dj(){
    memset(d,0x3f,sizeof d);
    d[t]=0;
    heap.push({0,t});
    
    while(!heap.empty()){
        PII t=heap.top();
        heap.pop();
        int id=t.second;
        int distance=t.first;
        if(vis[id])continue;
        vis[id]=1;
        for(int i=h[id];~i;i=ne[i]){
            int j=e[i];
            if(!vis[j] && d[j]>d[id]+1)d[j]=d[id]+1,heap.push({d[j],j}),cnt[j]=1;//你只有一条路可走,这条最短路非走不可
            else if(d[j]==d[id]+1)cnt[j]++;//这条路跟目前的最短路路径长度相等,你有多一种选择
        }
    }
}

signed main(){
    cin>>n>>m;
    memset(h, -1, sizeof h);
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        add(b,a);         
    }
    int k;
    cin>>k;
    for(int i=1;i<=k;i++)cin>>path[i];
    s=path[1],t=path[k];
    dj();
    
    for(int i=1;i<k;i++){
        if(d[path[i]] !=d[path[i+1]]+1)ansmax++,ansmin++;
        else if(cnt[path[i]]>1)ansmax++;
    }
    cout<<ansmin<<" "<<ansmax;
}

给定 n 个石子,编号为 1∼n。

其中第 i 个石子的价值为 ai。

你需要从中任意挑选若干个石子,并将挑选好的石子按照编号从小到大的顺序排成一排。

选中的石子在排好序后需要满足,对于任意两个相邻的石子(不妨设它们的编号为 x,y),x−y=ax−ay 均成立。

例如,当有 n=8 个石子,石子价值分别为 [3,4,4,6,6,7,8,9] 时,一些合理的选择方案如下:

选择 1,2,4 号石子,它们的价值分别为 3,4,6。1 号石子与 2 号石子相邻,2−1=4−3 成立。2 号石子与 4 号石子相邻,4−2=6−4 成立。所以方案合理。
选择 7 号石子。可以只选择一个石子,此时选取任何石子均为合理方案。
你的选择方案不仅需要合理,而且还要使得选中石子的价值总和尽可能大。

请计算并输出价值总和的最大可能值。

输入格式
第一行包含整数 n。

第二行包含 n 个整数 a1,a2,…,an。

输出格式
一个整数,表示选中石子的价值总和的最大可能值。

数据范围
前三个测试点满足 1≤n≤10。
全部测试点满足 1≤n≤2×105,1≤ai≤4×105。

输入样例1:
6
10 7 1 9 10 15
输出样例1:
26

#include<iostream>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#define ull unsigned long long
using namespace std;
int a[200010];
unordered_map<int,ull>s;
int main(){
    int n;
    cin>>n;
    int x;
    for(int i=1;i<=n;i++){
        cin>>x;
        s[x-i]+=x;
    }
    ull ans=0;
    for(auto [k,v]:s){
        ans=max(ans,v);
    }
    cout<<ans;
}

我们发现取出来的石子每两个相邻都要满足
x-y=ax-ay
也就是说我们取出来的所有石子都要满足
x-ax=y-ay
而且权值和要足够大
那么我们只需要把这所有的x-ax=一个常数t
每个t归到一类里面去就好了
如果题目给到权值是负数的话,就要在判断一次,不要放进hash table 哈希表就好了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值