BZOJ1018 [SHOI2008]堵塞的交通traffic && 2459: [BeiJing2011]神秘好人 (含datamaker)

24 篇文章 0 订阅
10 篇文章 0 订阅

题目

BZOJ1018 [SHOI2008]堵塞的交通traffic
BZOJ2459: [BeiJing2011]神秘好人

这两道题思路相仿所以放在一起

1018 题解

沃日啊代码毒瘤啊(不过好像没有想像中毒瘤?),写了一天多。
主要思想就是用线段树每个节点维护区间边界的四个点的连通信息,然后合并就是跑一遍只有八个点的无向图获取便边界四个点连通性。注意这里维护的信息只是通过内部节点的边。

回答询问的时候需要询问三个区间(c1 < c2),,[c1,c2] [1,c1],[c2,n],注意可以从两边绕过去,不一定只用[c1,c2]区间的边,一共有两种情况,同行,不同行,判一下就行了。

之前用的G[4][4]表示连通性,每次up就跑一遍8个点的floyd,然后T了,我居然还没一眼看出来为什么,8^3不爆炸才怪,然后改成用6个变量直接维护就变成近似O(1)了?up的时候讨论一下是从中间哪条边过去的就行了。

应该有WA不止的同学吧。放一个datamaker,但是这个东西造大数据的时候很慢,因为方法拙劣无比,操作数2000就差不多要造1s了。不过造小数据才能手调嘛。

1018 题目代码

//QWsin
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,x) for(int i=0;i<x;++i)
using namespace std;
const int maxn=100000+10;
const char msg[]={'N','Y'};
inline int read()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';
    return ret; 
}

int ok[maxn][2][2];//ok[i][j][k]表示i列j层往k方向连是否有边
//  0 * 1
//0...2 (4...6)
//1...3 (5...7)

struct NodeG{
    int g01,g02,g03,g12,g13,g23,l,r;
    NodeG(int l=0,int r=0):l(l),r(r){
        g01=g02=g03=g12=g13=g23=0;
    }
    NodeG operator + (const NodeG &rhs)const{
        NodeG t(l,rhs.r);

        int t1=ok[r][1][1],t2=ok[r][2][1];

        if(g01 || (g02&&rhs.g01&&g13&&t1&&t2)) t.g01=1;
        if((g02&&t1&&rhs.g02)||(g03&&t2&&rhs.g12)) t.g02=1;
        if((g02&&t1&&rhs.g03)||(g03&&t2&&rhs.g13)) t.g03=1;
        if((g12&&t1&&rhs.g02)||(g13&&t2&&rhs.g12)) t.g12=1;
        if((g12&&t1&&rhs.g03)||(g13&&t2&&rhs.g13)) t.g13=1;
        if((g23&&rhs.g02&&rhs.g13&&t1&&t2) || rhs.g23) t.g23=1;

        return t;
    }
};

#define mid ((l+r)>>1)
struct Node{
    NodeG g;//存放联通情况
    Node *lc,*rc;
    Node(int l,int r){lc=rc=NULL;g=NodeG(l,r);}
    inline void up(){g=lc->g+rc->g;}
}*root;

void build(Node* &p,int l,int r){
    p=new Node(l,r); 
    if(l==r) {p->g.g02=p->g.g13=1;return ;}//这两项是单列固定信息不会更改 
    build(p->lc,l,mid);build(p->rc,mid+1,r);p->up();
}

//竖列更新信息(单点修改)
void updata1(Node *p,int l,int r,int pos,int val)
{
    if(l==r){
        p->g.g01=p->g.g23=val;
        p->g.g03=p->g.g12=val;
        return ;
    }
    if(pos<=mid) updata1(p->lc,l,mid,pos,val);
    else updata1(p->rc,mid+1,r,pos,val);p->up();
}
//找到加边后影响到的合并(即加边位置为它的mid的那个区间),从这里开始重新合并
void updata2(Node *p,int l,int r,int pos,int val)
{
    if(mid==pos) {p->up();return ;}
    if(mid > pos) updata2(p->lc,l,mid,pos,val);
    else updata2(p->rc,mid+1,r,pos,val);
    p->up();
}

NodeG query(Node *p,int l,int r,int L,int R)
{
    if(L<=l&&r<=R) return p->g;
    if(R<=mid) return query(p->lc,l,mid,L,R);
    else if(mid<L) return query(p->rc,mid+1,r,L,R);
    else return query(p->lc,l,mid,L,R)+query(p->rc,mid+1,r,L,R);
}

int t1[4][4],t2[4][4],t3[4][4];

int n;
inline void query(int L,int R,int G[4][4])//复制一遍是因为按原来的邻接矩阵的方法,判断答案要方便一些,因为r1,c1,r2,c2都是变量
{
    NodeG t=query(root,1,n,L,R);
    G[0][1]=G[1][0]=t.g01;
    G[0][2]=G[2][0]=t.g02;
    G[0][3]=G[3][0]=t.g03;
    G[1][2]=G[2][1]=t.g12;
    G[1][3]=G[3][1]=t.g13;
    G[2][3]=G[3][2]=t.g23;
}

char op[20];
//询问只通过内部的点是否能连通(r1,c1)~(r2,c2) 

int main()
{
    cin>>n;
    build(root,1,n);

    int r1,c1,r2,c2;
    while(1)
    {
        scanf("%s",op);
        if(op[0]=='E') break;
        if(op[0]=='A') 
        {
            r1=read();c1=read();r2=read();c2=read();
            if(c1>c2) swap(c1,c2),swap(r1,r2);

            if(r1==1&&c1==1&&r2==2&&c2==3){
                int stop=1; 
            }

            if(r1==r2&&c1==c2) {printf("Y\n");continue;}

            query(c1,c2,t1);
            query( 1,c1,t2);
            query(c2, n,t3);

            int ans=t1[r1-1][r2+1];

            if(r1==r2){if(t2[2][3]&&t1[2-r1][4-r2]&&t3[0][1]) ans=1;}
            else{
                if(t2[2][3]&&t1[2-r1][1+r2]) ans=1;
                if(t3[0][1]&&t1[r1-1][4-r2]) ans=1;
            }
            putchar(msg[ans]);putchar('\n');
        }
        else
        {
            int val=op[0]=='O';
            r1=read();c1=read();r2=read();c2=read();
            if(r1==r2)
            {
                if(c1>c2)swap(c1,c2);
                ok[c1][r1][1]=ok[c2][r1][0]=val;
                updata2(root,1,n,c1,val);
            }
            else updata1(root,1,n,c1,val);
        }
    }
    return 0;
}

1018 datamaker

//QWsin
#include<set>
#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int n;

struct Road{
    int r1,c1,r2,c2;    
    Road(int r1,int c1,int r2,int c2):r1(r1),c1(c1),r2(r2),c2(c2){}
    bool operator < (const Road &rhs)const{
        if(r1!=rhs.r1) return r1<rhs.r1;
        if(c1!=rhs.c1) return c1<rhs.c1;
        if(r2!=rhs.r2) return r2<rhs.r2;
        return c2<rhs.c2;
    }
    inline void output(){
        printf("%d %d %d %d\n",r1,c1,r2,c2);    
    }
};

Road getRoad()
{
    int dir=rand()%2;
    if(dir)
    {
        int r=rand()%2+1;
        int c=rand()%(n-1)+1;
        return Road(r,c,r,c+1);
    }
    else
    {
        int c=rand()%n+1;
        return Road(1,c,2,c);
    }
}

set<Road>Roadset;

int main()
{
    srand(time(0));
    n=rand()*rand()%3+2;//点数
    printf("%d\n",n);
    int Roadcnt=0;
    for(int i=1;i<=20;++i)//20是操作数
    {
        int kind=rand()%3;
        if(kind==0&&Roadcnt==3*n-2) continue;
        if(kind==1&&Roadcnt==0) continue;

        if(kind==0)
        {
            printf("Open ");
            Road t=getRoad();
            while(Roadset.count(t)) t=getRoad();
            t.output();
            Roadset.insert(t);++Roadcnt;
        }
        else if(kind==1)
        {
            printf("Close ");
            Road t=getRoad();
            while(!Roadset.count(t)) t=getRoad();
            t.output();
            Roadset.erase(t);--Roadcnt;
        }
        else
        {
            printf("Ask ");
            int r1=rand()%2+1,r2=rand()%2+1;
            int c1=rand()%n+1,c2=rand()%n+1;
            printf("%d %d %d %d\n",r1,c1,r2,c2);
        }
    }
    printf("Exit");
    return 0;
}

2459 题解

其实跟上道题差不多,但是莫名其妙调了很久,关键就在于对于每个询问考虑的情况还要多一些,局限于上一道题的方法,所以大数据一直WA。

这道题要问最短路所以非得用邻接矩阵然后跑floyd了。所以常数好像很大。
询问的时候要枚举完最短路出现的情况,要不然会GG。

2459 题目代码

//QWsin
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,x) for(int i=0;i<x;++i)
using namespace std;
typedef long long ll;
const ll INF=1ll<<60;
const int maxn=100000+10;
const char msg[][10]={"N","Y"};
inline int read()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar())ret=ret*10+ch-'0';
    return ret;
}

ll _G[8][8];
ll ok[maxn][3],w[maxn];
//ok[i][j]表示i列j层往右方连的边权 
//w[i]表示第i列的边权 

struct NodeG{
    ll G[4][4];int l,r;
    //  0 * 1
    //0...2 (4...6)
    //1...3 (5...7)
    NodeG(int l=0,int r=0):l(l),r(r){
        rep(i,4)rep(j,4)
            if(i!=j)G[i][j]=INF;
            else G[i][j]=0;
    }
    inline NodeG operator + (const NodeG &rhs)const{

        NodeG ret;
        rep(i,8) rep(j,8) _G[i][j]=INF;
        rep(i,4) rep(j,4){
            _G[i][j]=G[i][j];
            _G[i+4][j+4]=rhs.G[i][j];
        }

        _G[2][4]=_G[4][2]=ok[r][1];
        _G[3][5]=_G[5][3]=ok[r][2];

        rep(k,8) rep(i,8) rep(j,8)
            _G[i][j]= min(_G[i][j],_G[i][k] + _G[k][j]);

        ret.G[2][0]=ret.G[0][2]=_G[0][6];
        ret.G[3][0]=ret.G[0][3]=_G[0][7];
        ret.G[2][1]=ret.G[1][2]=_G[1][6];
        ret.G[3][1]=ret.G[1][3]=_G[1][7];

        ret.G[1][0]=ret.G[0][1]=_G[0][1];
        ret.G[3][2]=ret.G[2][3]=_G[6][7];
        ret.l=l;ret.r=rhs.r;
        return ret;
    }
};

#define mid ((l+r)>>1)
struct Node{
    NodeG g;//存放联通情况
    Node *lc,*rc;
    Node(int l,int r){lc=rc=NULL;g=NodeG(l,r);}
    inline void up(){g=lc->g+rc->g;}
}*root;

void build(Node* &p,int l,int r){
    p=new Node(l,r); 
    if(l==r) 
    {
        //这几项是单列固定信息
        p->g.G[0][1]=p->g.G[1][0]=w[l];
        p->g.G[2][3]=p->g.G[3][2]=w[l];
        p->g.G[0][2]=p->g.G[2][0]=0;
        p->g.G[0][3]=p->g.G[3][0]=w[l];
        p->g.G[1][2]=p->g.G[2][1]=w[l];
        p->g.G[1][3]=p->g.G[3][1]=0;

        return ;
    }
    build(p->lc,l,mid);
    build(p->rc,mid+1,r);
    p->up();
}

//竖列更新信息 
void updata1(Node *p,int l,int r,int pos)
{
    if(l==r){
        p->g.G[0][1]=p->g.G[1][0]=w[pos];
        p->g.G[2][3]=p->g.G[3][2]=w[pos];
        p->g.G[0][2]=p->g.G[2][0]=0;
        p->g.G[0][3]=p->g.G[3][0]=w[pos];
        p->g.G[1][2]=p->g.G[2][1]=w[pos];
        p->g.G[1][3]=p->g.G[3][1]=0;
        return ;
    }
    if(pos<=mid) updata1(p->lc,l,mid,pos);
    else updata1(p->rc,mid+1,r,pos);p->up();
}

void updata2(Node *p,int l,int r,int pos)
{
    if(mid==pos) {p->up();return ;}
    if(mid > pos) updata2(p->lc,l,mid,pos);
    else updata2(p->rc,mid+1,r,pos);
    p->up();
}

NodeG query(Node *p,int l,int r,int L,int R)
{
    if(L<=l&&r<=R) return p->g;
    if(R<=mid) return query(p->lc,l,mid,L,R);
    else if(mid<L) return query(p->rc,mid+1,r,L,R);
    else return query(p->lc,l,mid,L,R)+query(p->rc,mid+1,r,L,R);
}

int n;

int main()
{
    cin>>n;
    for(int i=1;i<n;++i) ok[i][1]=read();
    for(int i=1;i<=n;++i) w[i]=read();
    for(int i=1;i<n;++i) ok[i][2]=read();

    build(root,1,n);

    int op,a,b,c;
    int m;cin>>m;
    while(m--)
    {
        op=read();
        if(!op)
        {
            a=read();b=read();c=read();
            if(a==1){
                w[b]=c;updata1(root,1,n,b);
            }
            else {
                ok[b][a?2:1]=c;
                updata2(root,1,n,b);
            }
        }
        else{
            a=read();b=read();
            int r1=((a&1)^1)+1,c1=(a-1)/2+1,r2=((b&1)^1)+1,c2=(b-1)/2+1;

            if(r1==r2&&c1==c2) {printf("0\n");continue;}
            if(c1>c2) swap(c1,c2),swap(r1,r2);

            NodeG t1=query(root,1,n,c1,c2);
            NodeG t2=query(root,1,n,1,c1);
            NodeG t3=query(root,1,n,c2,n);

            ll ans=t1.G[r1-1][1+r2];
            if(r1==r2){
                ans=min(ans,t2.G[2][3]+t1.G[2-r1][4-r1]+t3.G[1][0]);    
                ans=min(ans,t1.G[r1-1][4-r2]+t3.G[0][1]);
                ans=min(ans,t1.G[r2+1][2-r1]+t2.G[2][3]);
            }
            else {
                ans=min(ans,t3.G[0][1]+t1.G[2-r1][4-r2]+t2.G[2][3]);
                ans=min(ans,t2.G[2][3]+t1.G[2-r1][r2+1]);
                ans=min(ans,t3.G[0][1]+t1.G[r1-1][4-r2]);
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

2459 datamaker

//QWsin
#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MOD=10000;//边权范围
int main()
{
    int n=100000;//长度
    srand(time(0));
    printf("%d\n",n);
    for(int i=1;i<n;++i) printf("%d ",rand()%MOD+1);
    putchar('\n');
    for(int i=1;i<=n;++i) printf("%d ",rand()%MOD+1);
    putchar('\n');
    for(int i=1;i<n;++i) printf("%d ",rand()%MOD+1);
    putchar('\n');puts("");

    int m=100000;//操作数
    printf("%d\n",m);
    while(m--)
    {
        int op=rand()%2;
        if(!op)
        {
            printf("1 %d %d\n",rand()%(2*n)+1,rand()%(2*n)+1);
        }
        else 
        {
            int k=rand()%3;
            printf("0 %d ",k);
            if(k==0||k==2) printf("%d ",rand()%(n-1)+1);
            else printf("%d ",rand()%n+1);
            printf("%d\n",rand()%MOD+1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值