牛客练习赛61补题

比赛链接:https://ac.nowcoder.com/acm/contest/5026

D 最短路变短了
这题当时没想到,后来看了别人写的题解恍然大悟。解题思路就是如果一条边反向之后最短路变小了,肯定是用了这个反向边。如果用了这条边(假设这条边反向后的起点为 i,终点为 j,长度为 vij ),那么只有当 dis1-n > dis1-i + vij + disj-n 才能比反向前最短路距离小。先看 > 的左边,既然要和反向前最短路比较,那么肯定要把反向前最短路求出来。用 dijkstra 求出 1-n 的最短路后,我们再看 > 的右边。dis1-i 在我们求 1-n 最短路时已经求出来了,vij也是已知的,只有 disj-n还没有,所以现在只有一个问题,如何求出每个点的 disj-n
这里可以用反向建图实现,既然正向建图能求 1-n 的最短路能求出各点的 dis1-i,那么反向建图求 n-1的最短路就能求出各点的 disn-j ,这和正向建图的各点 disj-n 是一样的。
这样我们就求出了所有需要的条件,然后这题就AC啦。
代码如下

#include 
#include 
#include 
using namespace std;
const int maxn=2e5+5;
const int maxm=4e5+5;
const long long inf=2e18+5;
struct node
{
    int from,to,next,value;
}p[maxm];
struct Node
{
    int nd;
    long long len;
    friend bool operator < (Node a,Node b)
    {
        return a.len > b.len;
    }
};
long long dis[maxn];
int head[maxn];
int cnt,n,m,q;
void add(int x,int y,int v)
{
    p[++cnt].to=y;
    p[cnt].from=x;
    p[cnt].next=head[x];
    p[cnt].value=v;
    head[x]=cnt;
}
void dijkstra(int beg)
{
    priority_queue<Node>q;
    q.push({beg,0});
    dis[beg]=0;
    while(!q.empty())
    {
        Node tmp=q.top();
        q.pop();
        int u=tmp.nd;
        long long k=tmp.len;
        for(int i=head[u];i;i=p[i].next)
        {
            int v=p[i].to;
            int w=p[i].value;
            if(dis[v]>k+w)
            {
                dis[v]=k+w;
                q.push({v,dis[v]});
            }
        }
    }
}
int main()
{
    for(int i=0;i<maxn;i++)
    dis[i]=inf;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y,v;
        scanf("%d %d %d",&x,&y,&v);
        add(x,y,v);
    }
    dijkstra(1);
    for(int i=1;i<=m;i++)
    {
        add(p[i].to+n,p[i].from+n,p[i].value);
    }
    dijkstra(2*n);
    /*for(int i=1;i<=2*n;i++)
    cout<
    scanf("%d",&q);
    while(q--)
    {
        int u;
        scanf("%d",&u);
        int f=p[u].from;
        int t=p[u].to;
        int v=p[u].value;
        if(dis[n]>(dis[t]+dis[f+n]+v))
        puts("YES");
        else
        puts("NO");
    }
    return 0;
}

E 相似的子串
这题要用 二分 + 哈希,首先 x 满足单调性,所以很容易想到二分,那么怎么判断此时的值满不满足条件呢。我们可以从前往后找长度为 x 的字符串,这里明显贪心,因为要满足条件的话自然是越往前越好,这样可以使后面有更多的满足条件。所以我们要从前往后找。通过 hash 值来判断字符串,并记录在 unordered_map(map是O(logn)的查询,unordered_map是O(1)的查询),最后判断如果字符串个数够了就满足,不够就不满足。
代码如下

#include 
using namespace std;
typedef unsigned long long ull;
const int base=131;
const int maxn=2e5+5;
unordered_map<ull,pair<int,int> > mp;
ull Hash[maxn],Power[maxn];
char str[maxn];
char ch;
int n,k,l,r,mid,ans;
bool check(int x)
{
    mp.clear();
    for(int i=x;i<=n;i++)
    {
        ull haxi = Hash[i]-Hash[i-x]*Power[x];
        if(i-mp[haxi].first>=x)
        {
            mp[haxi].first=i;
            mp[haxi].second++;
        }
        if(mp[haxi].second>=k)
        return true;
    }
    return false;
}
int main()
{
    cin>>n>>k>>str+1;
    Power[0]=1;
    for(int i=1;i<=n;i++)
    {    
        Hash[i]=Hash[i-1]*base+(str[i]-'a')+1;
        Power[i]=Power[i-1]*base;
    }
    l=0;
    r=n/k+1;
    while(l<r)
    {
        mid=(l+r)>>1;
        if(check(mid))
        l=mid+1,ans=mid;
        else
        r=mid;
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(算法,二分图,最短路)