10.12 NOIP图论模拟考

T1

第一题很明显的差分约束……然而我居然TLE了???而且得了90分还是有运气成分的,下午再测时没有收改的程序导致把上午的题又被测了一遍,结果只得了80分……
所以为什么TLE呢……因为最后还加了一个二分……其实就是对差分约束的理解不够
因为

adde(i,i-1,0);
adde(i-1,i,1);

这么一个约束条件,所以整个图都是连通的,存在0~n的路径,不用二分加边!!
看来差分还需要(刷题?)再加强理解。

#include
#include
#include
using namespace std;
const int N=1000+5,M=10000+2000+5;
int to[M],w[M],nxt[M],head[N],etot;
int n,m;
void adde(int u,int v,int c){
    to[++etot]=v;
    nxt[etot]=head[u];
    w[etot]=c;
    head[u]=etot;
}
bool vis[N];
int SPFA(int st)
{
    int in[N],dis[N];;bool exi[N];
    memset(dis,32,sizeof(dis));
    memset(exi,0,sizeof(exi));
    memset(in,0,sizeof(in));
    queue<int> q;
    q.push(st);
    vis[st]=1;dis[st]=0;
    while(!q.empty()){
        int u=q.front();q.pop();
        exi[u]=0;
        for(int i=head[u];i!=-1;i=nxt[i]){
            int v=to[i];
            vis[v]=1;
            if(dis[v]>dis[u]+w[i]){
                dis[v]=dis[u]+w[i];
                in[v]++;
                if(in[v]>n-1) return 0;
                if(!exi[v]){
                    exi[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return -dis[0];
}
/*bool solve(int c)
{
    memset(vis,0,sizeof(vis));
    w[etot]=c;
    for(int i=0;i<=n;i++)
    if(!vis[i]) {
        if(!SPFA(i)) return 0;  
    }
    return 1;
}*/
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    memset(head,-1,sizeof(head));//可能会有0号点 
    scanf("%d%d",&n,&m);
    while(m--){
        int l,r,c;
        scanf("%d%d%d",&l,&r,&c);
        adde(r,l-1,-c);
    }
    for(int i=1;i<=n;i++){
        adde(i,i-1,0);
        adde(i-1,i,1);
    }
    printf("%d",SPFA(n));
    /*//s[n]-s[0]<=Mid
    //s[n]-s[0]<=inf 如果还是不行,说明这个条件本身就不成立  不过好像没有条件不成立的情况? 

    adde(0,n,0);
    int l=0,r=n;
    while(l>1;
        if(solve(mid)) //没有负环,条件成立,可以更小 
        r=mid;//(l,r]
        else l=mid;
    }
    printf("%d",r);*/
    return 0;
}

T2

T2想到了用并查集但是统计路径时有用了并查集,妥妥TLE……
那么正解就是——
在并查集时顺带更新路径。
del[]表示改点到其祖先节点的距离差。
最开始纠结了一阵子,在union(也就是把b所属集合加入a所属集合时)的时候要不要更改b集合中所有点的del[]呢?又怎么改所有点中的del[]呢?
然后发现,根本不用,只用更新fb的del值即可。因为在有关询问涉及到现b集合中的点时在getfa时del[b]会更新成正确值;如果没有涉及到b集合中的点那么del不会更新成正确值,反正用不到~

#include
#include
#include
using namespace std;
const int N=100000+5,M=100000+5;
int n,m;
int fa[N],del[N];//,dis[N];
//del[i] i与fa[i]的距离差 
int getfa(int x)
{
    if(fa[x]==x) return x;
    else {
        int fax=getfa(fa[x]);
        del[x]+=del[fa[x]];
        return fa[x]=fax;
    }
}
void unionn(int pa,int pb,int c)
{
    fa[pb]=pa;
    del[pb]=c;
}
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) fa[i]=i;
    while(m--){
        char s[5];
        scanf("%s",s);
        if(s[0]=='!'){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            int pa=getfa(a),pb=getfa(b);
            if(pa==pb) continue;
            unionn(pa,pb,del[a]+c-del[b]);
        }
        else {
            int a,b;
            scanf("%d%d",&a,&b);
            if(getfa(a)==getfa(b)){
                printf("%d\n",del[b]-del[a]);
            }   
            else printf("UNKNOWN\n");
        }
    }
    return 0;
}

T3

和火车调度的题面相同,但是数据范围十分BT

对于100%的数据,n,m<=100000,所有si的和不超过100000。

在这挂一下题解,因为我没有调这道题也应该不会调这道题了(进度紧啊没时间啊)

c
线段树优化建边的拓扑排序。注意到经停站把车站序列划分成了多个区间,每个区间对应O(log)个线段树上的节点,因此连边时可以把边数由O(nm)优化到O(m*log(n))。同时线段树的上下层节点也互相连边。具体细节可以参考标程。

T4

假设共用了m杯酒,酒精含量分别为b1,b2…bm
a1+…+am=m*n
a1+…+am=n+n+…+n(m个)
(a1-n)+(a2-n)+…+(am-n)=0
设bi=ai-n,可以预处理出来。
我们要做的就是用尽量少的bi使bi之和=0
那么就可以想到bfs啦(也可以用bfs做)
因为数据范围

对于100%的数据,0<=n,a_i<=1000,1<=k<=100000。

|bi|<=1000
所以可以剪枝~也一定要剪枝不然妥妥TLE

#include
#include
#include 
#include
using namespace std;
const int N=100000+5;
int n,k,a[N],b[N],dis[2000+5];bool exi[2005];
typedef pair<int,int> pii;
int bfs()
{
    queue q;
    q.push(make_pair(0,0));
    while(!q.empty()){
        pii x=q.front();q.pop();
        for(int i=1;i<=k;i++){
            if(!exi[x.first+b[i]+1000]&&abs(x.first+b[i])<=1000){
                if(!(x.first+b[i])) return x.second+1; 
                exi[x.first+b[i]+1000]=1;
                q.push(make_pair(x.first+b[i],x.second+1));
            } 
            if(x.second==1000) return -1;//咦这个剪枝对不对? 
        }
    }
    return -1;
}
int main()
{
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout); 
    scanf("%d%d",&n,&k);
    for(int i=1;i<=k;i++){
        scanf("%d",&a[i]);
        b[i]=a[i]-n;
    }
    printf("%d",bfs());
    return 0;
}

你可能感兴趣的:(------模拟考试-----,-------图论------)