zoukankan      html  css  js  c++  java
  • 2019.11.02(CSP模拟)(次短路+线段树(扫描线思想)+背包/hash)

     

     

    因为数据很水,bfs都可以过。

    然后后面数据被加强了,所以还是来看看正解吧!

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    #define N 1003
    #define M 100003
    #define re register  
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    void print(int x)
    {
        if(x<0)x=-x,putchar('-');
        if(x>9)print(x/10);
        putchar(x%10+'0');
    }
    struct EDGE{
        int nextt,to,no;
    }w[M];
    struct edge{
        int x,y;
    }e[M];
    struct qnode{
        int u,op;
    };
    int n,m;
    int tot=0;
    int head[N],f[N][2],t[N][3],ans[N][N];//记录最长次长的距离和到达的节点 
    void add(int a,int b)
    {
        tot++;
        w[tot].nextt=head[a];
        w[tot].to=b;
        head[a]=tot;
    }
    queue<qnode>q;
    void work(int S)
    {
        for(re int i=1;i<=n;++i)
          for(re int j=0;j<=1;++j)
            f[i][j]=-1,t[i][j]=0;
        while(!q.empty())q.pop();
        for(re int i=head[S];i;i=w[i].nextt)
        {
            re int v=w[i].to;
            f[v][0]=1;t[v][0]=v;
            q.push(qnode{v,0});
        }
        while(!q.empty())
        {
            int x=q.front().u;
            int op=q.front().op;q.pop();
            for(re int i=head[x];i;i=w[i].nextt)
            {
                re int v=w[i].to;
                if(v==S)continue;
                if(f[v][0]==-1)
                {
                    f[v][0]=f[x][op]+1;
                    t[v][0]=t[x][op];
                    q.push(qnode{v,0});
                }
                else
                {
                    if(f[v][1]!=-1||t[v][0]==t[x][op])continue;
                    f[v][1]=f[x][op]+1;
                    t[v][1]=t[x][op];
                    q.push(qnode{v,1});
                }
            }
        }
    }
    int main()
    {
        freopen("railway.in","r",stdin);
        freopen("railway.out","w",stdout);
        n=read();m=read();
        for(re int i=1;i<=m;++i)
        {
            e[i].x=read();
            e[i].y=read();
            add(e[i].x,e[i].y);
        }
        for(re int i=1;i<=n;++i) 
        {
            work(i);
            for(re int j=1;j<=n;++j)
              ans[i][j]=f[j][1];
        }
        for(int i=1;i<=m;++i)
        {
            print(ans[e[i].x][e[i].y]);
            putchar(' ');
        }
        putchar('
    ');
    }
    T1

     

    下面我们把那n个矩阵称为修改矩阵,查询的称为查询矩阵。

    求两个矩阵的交我们可以把修改矩阵转为两个前缀矩阵相减。

    下面画图说一下具体过程(灵魂画手上线)(这里%一下cjr大佬):

    我们首先把所有的修改矩阵和查询矩阵按照x从小到大排序。

    然后扫描的从左往右扫。

    这幅图我们会先扫到修改矩阵的左端。

    显然交就是中间那一块。

    然后我们把修改矩阵分为两个前缀矩阵s1和s2

     此时我们知道s1在y轴上占有下图括号括起来的宽度,面积为sum(用线段树维护即可)

     然后继续往后扫。扫到了查询矩阵的左端,我们可以对答案做出贡献。

    在线段树上查一下,发现len和sum如上图所示。

    然后ans更新一下为len*x - sum(负的,因为此时查询矩阵端点为负)。也就是中间那一坨。可以说是查询矩阵的前缀矩阵与修改前缀矩阵的交。

    然后再往后扫,扫到了修改矩阵的右端点,我们再更新线段树上的信息。

    此时线段树上的len就为0了,sum是下图涂红的那一块(注意是负的)。

    因为前面还有一个正的sum,两个sum合起来就是下图涂红的那一块(注意此时sum还是负的)。

    再往后扫到了查询矩阵的右端点,又可以更新答案。

    len*x-sum就是下面这样(len为0,-sum因为sum本身是负的,-sum就是正的)

    然后我们就求出交了。

    很巧妙啊(反正我想不到的)。

    代码就是简单的线段树了

    #include<bits/stdc++.h>
    #define N 500003
    #define LL long long
    #define INF 2100000000
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    struct EDGE{
        int l,r,h,id; 
        int f; //f标记1,-1
        bool operator<(const EDGE &a)const{return h<a.h;}
    }w[N*4];
    struct data{LL sum;int len;};//区间和//区间长度 
    struct Node{//这里线段树开个结构体比较方便 
        int l,r;//左右儿子区间 
        int t2;
        LL t1;
        data res;
    }q[N*4];
    LL ans[N];
    void build(int k,int l,int r)
    {
        q[k].l=l;q[k].r=r;
        if(l==r) return;
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
    }
    data merge(data a,data b)
    {
        return (data){a.sum+b.sum,a.len+b.len};
    }
    void pushdown(int k,int l,int r)
    {
        int mid=(l+r)>>1;
        int lc=k<<1,rc=k<<1|1;
          q[lc].t1+=q[k].t1;q[lc].t2+=q[k].t2;
          q[lc].res.sum+=(mid-l+1)*q[k].t1;
          q[lc].res.len+=(mid-l+1)*q[k].t2;
        q[rc].t1+=q[k].t1;q[rc].t2+=q[k].t2;
        q[rc].res.sum+=(r-mid)*q[k].t1;
          q[rc].res.len+=(r-mid)*q[k].t2;
        q[k].t1=0;q[k].t2=0;
    }
    void modify(int k,int l,int r,LL val,int f)
    {    
        if(q[k].l>=l&&q[k].r<=r)
        {
            q[k].t1+=val*f;
            q[k].t2+=f;
            q[k].res.sum+=val*f*(q[k].r-q[k].l+1);
            q[k].res.len+=f*(q[k].r-q[k].l+1);
            return;
        }
        int mid=(q[k].l+q[k].r)>>1;
        pushdown(k,q[k].l,q[k].r);
        if(l<=mid)modify(k<<1,l,r,val,f);
        if(r>mid)modify(k<<1|1,l,r,val,f);
        q[k].res=merge(q[k<<1].res,q[k<<1|1].res);
    }
    data query(int k,int l,int r)
    {
        data tmp={0,0};
        if(q[k].l>=l&&q[k].r<=r)return q[k].res;
        int mid=(q[k].l+q[k].r)>>1;
        pushdown(k,q[k].l,q[k].r);
        if(l<=mid)tmp=query(k<<1,l,r);
        if(r>mid)tmp=merge(tmp,query(k<<1|1,l,r));
        return tmp;
    }
    int n,m;
    int main()
    {
    //    freopen("intersec.in","r",stdin);
    //    freopen("intersec.out","w",stdout);
        n=read();m=read();
        int W=read(),L=read();
        int maxn=-INF;
        int tot=0;
        for(int i=1;i<=n;++i)
        {
            int x1=read(),y1=read(),x2=read(),y2=read();
            w[++tot]=(EDGE){y1+1,y2,x1,0,1};
            w[++tot]=(EDGE){y1+1,y2,x2,0,-1};  
            maxn=max(maxn,y2);
        }
        for(int i=1;i<=m;++i)
        {
            int x1=read(),y1=read(),x2=read(),y2=read();
            w[++tot]=(EDGE){y1+1,y2,x1,i,-1};
            w[++tot]=(EDGE){y1+1,y2,x2,i,1};  
        }    
        sort(w+1,w+tot+1);
        build(1,0,maxn);
        for(int i=1;i<=tot;++i)
        {
            if(!w[i].id)modify(1,w[i].l,w[i].r,w[i].h,w[i].f);
            else
            {
                data tmp=query(1,w[i].l,w[i].r);
                ans[w[i].id]+=w[i].f*((LL)tmp.len*w[i].h-tmp.sum);
            }
        }
        for(int i=1;i<=m;++i)printf("%lld
    ",ans[i]);
    } 
    /*
    2 2
    6 6
    1 1 3 3
    4 2 5 4
    2 2 6 3
    1 1 6 6
    
    */
    T2

    然后在lemon上测的时候不知道为什么第三个点RE了(第三个点???)

    如果有大佬能指点一下就感激不尽了!(这就是为什么我这一篇没有设密码的原因)


     

     

    先上题解!

     

    我这里主要是想说一下为什么方程是这个亚子的。

    增加或删除一个数,指的是从还需要选的数的集合里删去。

    增加很好理解,因为多了一个数j,所以i--->i+1,x--->x+j。

    删除是因为把j从备选集合里删去了,本来它会对选出的数有贡献的,现在删去就要把贡献减去。

    背包我们可以先预处理一下,每次枚举这一位填什么数的时候,看背包数方案是否为0.

    如果为0说明这一位不能为j,继续枚举,不为0这说明这一位为j是合法的,同时把j从备选集合里删去。

    因为集合里是还没确定位置的数。

    j从9到0枚举保证最后答案最大。

    #include<bits/stdc++.h>
    #define N 1003
    #define LL long long
    #define mod 998244353
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    void print(int x)
    {
        if(x<0)x=-x,putchar('-');
        if(x>9)print(x/10);
        putchar(x%10+'0');
    }
    int len,num=0;
    int flagg=0;
    int tong[13],f[N][13];
    int tot=0;
    int SUM=0;
    char s[N];
    void add(int x)
    {
        for(int i=num;i>=0;--i)
          for(int j=0;j<11;++j)
            f[i+1][(j+x)%11]=(f[i+1][(j+x)%11]+f[i][j])%mod;
        tong[x]++;num++;
    }
    void del(int x)
    {
        for(int i=0;i<=num;++i)
          for(int j=0;j<11;++j)
            f[i+1][(j+x)%11]=(f[i+1][(j+x)%11]-f[i][j]+mod)%mod;
        tong[x]--;num--;
    }
    int main()
    {
        freopen("sort.in","r",stdin);
        freopen("sort.out","w",stdout);
        scanf("%s",s+1);
        len=strlen(s+1);
        f[0][0]=1;
        for(int i=1;i<=len;++i)
        {
            SUM=(SUM+s[i]-'0')%11;
            add(s[i]-'0');
        }
        int half=SUM*6%11;//6是2的inv 
        int s0=half,s1=half;
        for(int i=1;i<=len;++i)
        {
            int S,pos;
            if(i%2==1)pos=len/2-i/2,S=s0;
            else pos=(len+1)/2-(i+1)/2,S=s1;//当前是奇数说明要找下一位偶数的背包 
            int j;
            for(j=9;j>=0;--j)
            {
                if(!tong[j])continue;
                del(j);
                if(f[pos][S])break;
                add(j);
            }
            if(i%2==1)s1=(s1-j+11)%11;
            else s0=(s0-j+11)%11;
            putchar(j+'0');
        }
    }
    T3
  • 相关阅读:
    Shodan新手入坑指南
    linux 下查看网卡工作速率
    centos关闭ipv6
    springBoot----@ConditionalOnxxx相关注解总结
    ElasticSearch Root身份运行
    CentOS6 Install kafka
    CentOS 7 中firewall-cmd命令
    sensu
    metrics+spring+influxdb
    SpringBoot(十二):SpringBoot整合Kafka
  • 原文地址:https://www.cnblogs.com/yyys-/p/11788325.html
Copyright © 2011-2022 走看看