DAY 7 上午

时间:2019-08-13
本文章向大家介绍DAY 7 上午,主要包括DAY 7 上午使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一些图论的题目


BZOJ 3445 Roadblock

求出最短路,枚举每条边再跑一遍即可(科技为了我

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n,m;

int head[105],cnt=1;
struct edge
{
    int dis,to,nxt;
}edg[10005];

inline void add(int u,int v,int w)
{
    edg[++cnt].to=v;
    edg[cnt].dis=w;
    edg[cnt].nxt=head[u];
    head[u]=cnt;
    edg[++cnt].to=u;
    edg[cnt].dis=w;
    edg[cnt].nxt=head[v];
    head[v]=cnt;
}

ll diss[105];
bool vis[105];
int pre[105];
ll qwq;
ll ans;

inline void dij()
{
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
    q.push(make_pair(0,1));
    for(int i=1;i<=n;i++) diss[i]=99999999999;
    diss[1]=0;
    while(!q.empty())
    {
        int now=q.top().second;
        q.pop();
        if(vis[now]) continue;
        vis[now]=1;
        for(int i=head[now];i;i=edg[i].nxt)
        {
            int v=edg[i].to;
            if(diss[now]+edg[i].dis<diss[v])
            {
                diss[v]=diss[now]+edg[i].dis;
                q.push(make_pair(diss[v],v));
                pre[v]=i;
            }
        }
    }
    qwq=diss[n];
}

inline void dijnew()
{
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
    q.push(make_pair(0,1));
    for(int i=1;i<=n;i++) diss[i]=99999999999,vis[i]=0;
    diss[1]=0;
    while(!q.empty())
    {
        int now=q.top().second;
        q.pop();
        if(vis[now]) continue;
        vis[now]=1;
        for(int i=head[now];i;i=edg[i].nxt)
        {
            int v=edg[i].to;
            if(diss[now]+edg[i].dis<diss[v])
            {
                diss[v]=diss[now]+edg[i].dis;
                q.push(make_pair(diss[v],v));
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    dij();
    for(int i=1;i<=n;i++)
    {
        int t=pre[i];
        edg[t].dis<<=1;edg[t^1].dis<<=1;
        dijnew();
        ans=max(ans,diss[n]);
        edg[t].dis>>=1;edg[t^1].dis>>=1;
    }
    cout<<ans-qwq;
}

建一个分层图

原图在第一层

第一层向第二层连有向边

对于u-->v,从第一层的u点向第二层的v点连一条长度为0的边

优于更新有可能用不到k次

从上一层的点u到下一层的点u连一条长度为0的边,表示不用这次更新

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n,m,k;

int head[210005],cnt;
struct edge
{
    int dis,to,nxt;
}edg[4200005];

inline void add(int u,int v,int w)
{
    edg[++cnt].to=v;
    edg[cnt].dis=w;
    edg[cnt].nxt=head[u];
    head[u]=cnt;

}

ll diss[210005];
bool vis[210005];
ll ans;

inline void dij()
{
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
    q.push(make_pair(0,1));
    for(int i=1;i<=n*(k+1);i++) diss[i]=9999999999;
    diss[1]=0;
    while(!q.empty())
    {
        int now=q.top().second;
        q.pop();
        if(vis[now]) continue;
        vis[now]=1;
        for(int i=head[now];i;i=edg[i].nxt)
        {
            int v=edg[i].to;
            if(diss[now]+edg[i].dis<diss[v])
            {
                diss[v]=diss[now]+edg[i].dis;
                q.push(make_pair(diss[v],v));
            }
        }
    }
}



int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
        for(int j=1;j<=k;j++)
        {
            add(j*n+u,j*n+v,w);
            add(j*n+v,j*n+u,w);
            add((j-1)*n+u,j*n+v,0);
            add((j-1)*n+v,j*n+u,0);
        }  
    }
    dij();
    ll ans=diss[n];
    for(int i=1;i<=k;i++)
    {
        ans=min(ll(ans),diss[i*n+n]);
    }
    cout<<ans;
    
}

min(x3-x1,y3-y1)=x3-x1>=min(x2-x1,y2-y1)+min(x3-x2,y3-y2)

按x轴排序,相邻的点建边,然后建O(n)条

跑dij就行了

小边越大,最大边越大

最小边变大之后,最大边能选取的范围变小了,且有可能会变大

固定最小边,最小化最大边
并查集维护连通性

先把最小边拿出来,看看能不能加进去,重复操作,直到s和t联通

把最小边从大往小枚举

每次新的边可用的时候,如果连通块正好联通就可以

如果会出环,那么找到u和v之前的路径上最大的边

对于枚举的最小边,看看最大边是谁

1.二分答案

所有长度<=mid的边属于部落内部

>mid的边属于部落外部

看看有多少个连通块

如果连通块数比k多就可行

2.kruscal

n^2连边

把部落看成连通块

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n,k;
int fa[1005];
int cnt;
double ans;

struct edge
{
    int from,to;
    double dis;
}edg[1000005];

struct node
{
    int x,y;
}nod[1005];

inline bool cmp(edge a,edge b)
{
    return a.dis<b.dis;
}

inline int find(int x)
{
    while(x!=fa[x]) x=fa[x]=fa[fa[x]];
    return x;
}

/*inline void kruscal()
{
    int t=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=cnt;i++)
    {
        if(t==n-k)
        {
            ans=edg[i+1].dis;
            return;
        }
        if(find(edg[i].to)!=find(edg[i].from))
        {
            fa[find(edg[i].to)]=find(edg[i].from);
            t++;
        }
    }

}*/

inline void kruscal()
{
    int t=0;
    bool flag=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=cnt;i++)
    {
        if(t==n-k)
        {
            flag=1;
        }
        if(find(edg[i].to)!=find(edg[i].from))
        {
            fa[find(edg[i].to)]=find(edg[i].from);
            t++;
            if(flag==1)
            {
                ans=edg[i].dis;
                return ;
            }
        }
        
    }

}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d%d",&nod[i].x,&nod[i].y);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            edg[++cnt].from=i;
            edg[cnt].to=j;
            edg[cnt].dis=sqrt((nod[i].x-nod[j].x)*(nod[i].x-nod[j].x)+(nod[i].y-nod[j].y)*(nod[i].y-nod[j].y));
        }
    }
    sort(edg+1,edg+1+cnt,cmp);
    kruscal();
    printf("%.2lf",ans);
}

选一条边表示两个点属于同一个连通块

目标就是选若干条边使得图分成k个连通块,并且没有选中的边的最小值尽可能大

kruscal选中n-k条边就停止

知道了一个点的奇偶性就可以看出来它有没有

1,1~2,1~3,...,1~n的奇偶性就可以知道所有的奇偶性

前缀和任何一个可行的方案一定可以推出s1~sn

如果我询问了l~r,则我可以知道s[r]-s[l-1]的奇偶性

%2意义下加法就是异或

有传递性

如果我知道s[x]和s[y]奇偶相同,s[y]和s[z]奇偶相同,则s[x]和s[z]奇偶相同

已经知道s[0]=0

目的就是s[1]~s[n]的所有点和s[0]联通起来

有n^2条边,n+1个点,跑最小生成树就行了

贪心策略:在每一个点去往最近的加油站

以所有加油站为源点跑多源最短路,顺便维护最初的源点,现在知道每一个加油站离他最近的源点

可以看成在加油站之间的移动

枚举原图的边,如果两点最近加油站不同,就连边

对加油站跑最小生成树

留下最短路图的公共部分

留下有向边

一定无环(DAG)

求dag上最长路,拓扑+dp

设a1是最小的,在模a1意义下一个数的所有取值只有0~a1-1

而如果 k 是可被表示的,那么 k + a[1], k + 2 × a[1], . . . 都可被表示。故问题转化为求解每个位置最小可被表示数字。建a1个点

x-->(x+aj)%ai(相当于每次+aj)

就可以转移模数

希望走的边的长度尽可能小

大于这个东西就可以表示

加边之后从零点跑单源最短路

线形基

给你若干个int,把他们抑或起来,最后异或出来的数是有限的

发现原来的数是很冗余的,只需要其中某些数就可以达到原来的效果

1 xor 2 = 3 1,2,3是线性相关的  或者说 1 xor 2 xor 3 = 0

就是说保留两个就可以出来第三个

线性基是拟阵

遗传性,交换性

每一个数分配权值

给你一个集合

找到一个子集使得它的权值最小并且是线性基

怎么解释生成森林和线性基对应

每一条边对应二进制

当且仅当环异或起来是0

k条边最短路

具有结合律

可以求出A^1,A^2,A^4...

也可以快速幂

O(n^3logn)

应用:

给你一张图,判断最小的负环

求A^1,A^2,A^3...有没有负环,如果有的话就是最小的

可以二分优化

用倍增的思想往上跳

原文地址:https://www.cnblogs.com/lcezych/p/11348416.html