zoukankan      html  css  js  c++  java
  • HGOI20190810 省常中互测3

      Problem A  夏洛特

    若当前处在点$(x,y)$下一时刻可以向该点四周任意方向走动一步,

    初始在$(0,0)$是否存在一条合法的路线满足下列$n$个限制:

    每一个限制形如$t_i , x_i , y_i$表示第$t_i$时刻,需要在点$(x_i , y_i)$ 处

    输出"YES"或者"NO",有$T(Tleq 10)$组数据。

    对于$100\%$的数据满足$n leq 10^5 ,0 leq x_i,y_i leq 10^5 , 0 leq t_i leq 10^7$

    Sol: 首先按照时间先后排序,每次考虑从$t_0 , x_0 ,y_0$的限制走到$t , x, y$的限制、

      显然,从$(x_0,y_0)$走到$(x,y)$至少需要$|x-x_0|+|y-y_0|$步,

      于是令$res = t - t_0 - (|x-x_0|+|y-y_0|)$ 表示剩余需要浪费的步数。

      显然,如果剩余步数为偶数那么可以上下、上下的构造出合法解,若剩余步数为奇数则没有办法构造。

      上述算法的复杂度是$O(T n log_2 n)$

    # include <bits/stdc++.h>
    # define int long long 
    using namespace std;
    const int N=2e5+10;
    struct rec{
        int t,x,y;
    }a[N];
    int n;
    bool cmp(rec a,rec b){return a.t<b.t;}
    bool work()
    {
        int t=0ll,x=0ll,y=0ll;
        for (int i=1;i<=n;i++) {
            int res=a[i].t-t-abs(a[i].x-x)-abs(a[i].y-y);
            if ((res < 0ll) || (res & 1ll)) return false;
            t=a[i].t; x=a[i].x; y=a[i].y; 
        }
        return true;
    }
    inline int read()
    {
        int X=0,w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    }
    signed main()
    {
      freopen("charlotte.in","r",stdin);
      freopen("charlotte.out","w",stdout);
        int T=read(); 
        while (T--) {
            n=read();
            for (int i=1;i<=n;i++) a[i].t=read(),a[i].x=read(),a[i].y=read();
            sort(a+1,a+1+n,cmp); 
            puts(work()?"Yes":"No");
        }
        return 0;
    }
    A.cpp

      Problem B 西比拉先知系统

      给出一幅无向图$G$,初始每个点的点权为$0$,维护下面$2$个操作:

      0 x  : 表示询问点$x$的点权。

      1 x y : 表示将点$x$和与点$x$直接相连的点权加上$y$

      Subtask1 :任意两个点之间一定定满足不连通或存在恰好一条路径

      Subtask2:除了1号节点以外,所有节点度数小于$10$

      对于$100\%$的数据,$n,m,Q leq  3 imes  10^5 , y leq 10^3$

      Sol: 考场没$A$,被卡常了,$95 pts$ 

      考虑部分分怎么做,

      Subtask1 : 保证$G$是森林 , 对每一棵树求出bfs序,使得直接儿子的编号是连续的。

            由于父亲只有一个,那么在加权的时候再额外的把父亲的权加上即可。

            树状数组维护区间加,单点求值。 复杂度是$O(m log_2 n)$

          Subtask2 : 对于$1$号节点稍微特判一下,若不是一号节点的进行操作$2$直接暴力。

            如果是$1$号节点进行操作$2$那么只需要打一个标记即可.

            对于询问操作,如果这个点是$1$或者与$1$直接相连那么除了自己维护的那个真实值以外还需要加上$1$节点的标记值。

            这样的复杂度是$O(10 imes m)$

         然后这样就可以写出$73 pts $的代码了。

    # include<bits/stdc++.h>
    using namespace std;
    int n,m,q;
    vector<int>E[300010];
    inline int read()
    {
        int X=0,w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    }
    void write(int x)
    {
        if (x<0) putchar('-'),x=-x;
        if (x>9) write(x/10);
        putchar('0'+x%10);
    }
    void writeln(int x)
    {
        write(x); putchar('
    ');
    }
    bool vis[300010],flag;
    void cir(int u,int fa)
    {
        vis[u]=1;
        for (int i=0;i<(int)E[u].size();i++) {
            int v=E[u][i]; if (v==fa) continue;
            if (vis[v]) { flag=0; return;}
            cir(v,u);
        }
    }
    bool check1()
    {
        memset(vis,false,sizeof(vis));
        for (int i=1;i<=n;i++) 
         if (!vis[i]) {
            flag=1; cir(i,0);
            if (!flag) return false;
        }
        return true;
    }
    
    namespace Subtask1 {
        const int N=3e3+10;
        int val[N];
        void update(int u,int value)
        {
            val[u]+=value;
            for (int i=0;i<(int)E[u].size();i++) {
                int v=E[u][i];
                val[v]+=value;
            }
        }
        void main() {
            memset(val,0,sizeof(val));
            for (int i=1;i<=q;i++) {
                int op=read(),x=read();
                if (op) { int y=read(); update(x,y);}
                else writeln(val[x]);
            }
            exit(0);
        }
    }
    namespace Subtask2 {
        const int N=3e5+10;
        int bfsn[N],c[N],R[N],f[N];
        # define lowbit(x) (x&(-x))
        void update(int x,int d) { for (;x<=bfsn[0];x+=lowbit(x)) c[x]+=d;}
        void modify(int l,int r,int d){update(l,d); update(r+1,-d);}
        int query(int x) { int ret=0; for (;x;x-=lowbit(x)) ret+=c[x]; return ret;}
        # undef lowbit
        void bfs(int u,int fa) {
            f[u]=fa; R[u]=bfsn[u];
            for (int i=0;i<(int)E[u].size();i++) {
                int v=E[u][i]; if (v==fa) continue;
                bfsn[v]=++bfsn[0];
                R[u]=bfsn[v];
            }
            for (int i=0;i<(int)E[u].size();i++) {
                int v=E[u][i]; if (v==fa) continue;
                bfs(v,u);
            }
        }
        void main() {
            memset(bfsn,0,sizeof(bfsn));
            for (int i=1;i<=n;i++)
             if (!bfsn[i]) bfsn[i]=++bfsn[0],bfs(i,0);
            for (int i=1;i<=q;i++) {
                int op=read(),x=read();
                if (op) {
                    int y=read();
                    if (f[x]) {
                        modify(bfsn[f[x]],bfsn[f[x]],y);
                    }
                    modify(bfsn[x],R[x],y);
                } else writeln(query(bfsn[x]));
            }
        }
    }
    namespace Subtask3 {
        const int N=3e5+10;
        int add,val[N];
        bool mark[N];
        void update(int u,int value) {
            val[u]+=value;
            for (int i=0;i<(int)E[u].size();i++) {
                int v=E[u][i];
                val[v]+=value;
            }
        }
        void main() {
            for (int i=0;i<(int)E[1].size();i++) mark[E[1][i]]=1;
            for (int i=1;i<=q;i++) {
                int op=read(),x=read();
                if (op) {
                    int y=read();
                    if (x==1) val[1]+=y,add+=y;
                    else update(x,y);
                } else writeln(val[x]+(mark[x]?add:0));
            }
        }
    }
    int main()
    {
        n=read();m=read();q=read();
        for (int i=1;i<=m;i++) {
            int u=read(),v=read();
            if (u==v) continue;
            E[u].push_back(v);
            E[v].push_back(u);
        }
        for (int i=1;i<=n;i++) {
            sort(E[i].begin(),E[i].end());
            E[i].erase(unique(E[i].begin(),E[i].end()),E[i].end());
        }
        if (n<=3000 && m<=3000 && q<=3000) Subtask1::main();
        else if (check1()) Subtask2::main();
        else Subtask3::main();
        return 0;
    }
    B_73pts.cpp

     现在来考虑正解,试图推广一下Subtask2的做法,如果一个点进行了操作$1$,事实上我们不需要将所有和它相连的边都跑过去,而是使用一个$tag$标记来记录一下这个点被加了多少。

     把原来的无向图看成有向图,对于原来所有无向边,转化为度较小的点向度较大的点连单向边。

     对于一个$1$操作我们只需要将当前点的$tag$标记加上,然后把单向边中它指向的节点的val值更新即可。

     对于一个$0$操作我们只需要将该点指向的所有元素的$tag$加上然后加上该点的$val$值即可。

     对于一次操作,复杂度应该为两个点之间的较小的度。这个东西当完全图的时候可以最大化,所以极限是$sqrt{n}$

     综上所述,我们的算法的复杂度是$O(m sqrt{n})$ ,并且是一个极其不满的上界。

    # include <bits/stdc++.h>
    using namespace std;
    const int N=3e5+10;
    struct edge{
        int u,v;
    }e[N];
    int n,m,q,val[N],du[N],tag[N];
    vector<int>E[N];
    int main()
    {
        scanf("%d%d%d",&n,&m,&q);
        for (int i=1;i<=m;i++)
         scanf("%d%d",&e[i].u,&e[i].v),
         du[e[i].u]++,du[e[i].v]++;
        for (int i=1;i<=m;i++)
         if (du[e[i].u]<du[e[i].v]) E[e[i].u] .push_back(e[i].v);
         else E[e[i].v] .push_back(e[i].u);
        while (q--) {
            int op,x; scanf("%d%d",&op,&x);
            if (op==0) {
                int ret=val[x];
                for (int i=0;i<E[x].size();i++) {
                    int v=E[x][i]; ret+=tag[v];
                }
                printf("%d
    ",ret);
            } else {
                int y; scanf("%d",&y); 
                for (int i=0;i<E[x].size();i++) {
                    int v=E[x][i]; val[v]+=y;
                }
                val[x]+=y; tag[x]+=y;
            }
        } 
        return 0;
    }
    B.cpp

      Problem C 替身使者

      给出一个$5$次多项式$G(x) = sumlimits_{i=1} ^ 5 a_i imes x^i $

      其第$i$次项的系数$a_i$满足$0 leq a_i leq 10$ 

      给出若干条线段$[l_i,r_i] , 1 leq l_i leq r_i leq m$,可以在这些线段中的任意一个点放一个替身使者。

      最大化每个点G(替身使者数目)的和。

      对于$100 \%$ 的数据$1 leq m leq 10^7 , 1 leq n leq 250$

    Sol : 观察到线段的长度和答案没有任何关系,离散化即可。

       值域为$[1,500]$,设$f[l][r]$表示在值域$[l,r]$的答案为多少。

       那么显然设$T$为值域右边界,答案就是$f[1][T]$

           区间dp,设在第$i$个位置放尽可能多的替身使者,问题就转化为两个子问题$f[l][i-1] , f[i+1][r]$

          这样子增加的贡献值就是$G(num_i)$ ,复杂度是$O(n^3)$

    # include <bits/stdc++.h>
    # define int long long
    using namespace std;
    const int N=605;
    struct rec{ int l,r;}a[N];
    int f[N][N],n,m,v[5],c[N],T;
    vector<int>t;
    int fun(int x){return v[0]*x+v[1]*x*x+v[2]*x*x*x+v[3]*x*x*x*x+v[4]*x*x*x*x*x;}
    int dfs(int l,int r)
    {
        if (l>r) return 0;
        if (f[l][r]!=-1) return f[l][r];
        vector<int>v; v.resize(r-l+1);
        for (int i=1;i<=n;i++)
         if (a[i].l>=l && a[i].r <= r) {
            v[a[i].l-l]++;
            if (a[i].r<r)v[a[i].r-l+1]--;
         }
         int ans=0,now=0;
         for (int i=0;i<r-l+1;i++) {
            now+=v[i];
            ans=max(ans,dfs(l,l+i-1)+dfs(l+i+1,r)+fun(now));
         }
         return f[l][r]=ans;
    }
    signed main()
    {
        scanf("%lld%lld",&n,&m);
        for (int i=0;i<5;i++) scanf("%lld",&v[i]);
        for (int i=1;i<=n;i++) {
            scanf("%lld%lld",&a[i].l,&a[i].r);
            t.push_back(a[i].l); t.push_back(a[i].r);
        }
        sort(t.begin(),t.end());
        T=unique(t.begin(),t.end())-t.begin();
        for (int i=1;i<=n;i++)
         a[i].l=lower_bound(t.begin(),t.begin()+T,a[i].l)-t.begin()+1,
         a[i].r=lower_bound(t.begin(),t.begin()+T,a[i].r)-t.begin()+1;   
        memset(f,-1,sizeof(f));
        int ans = dfs(1,T);
        printf("%lld
    ",ans); 
        return 0;
    }
    C.cpp
  • 相关阅读:
    Leetcode 349. Intersection of Two Arrays
    hdu 1016 Prime Ring Problem
    map 树木品种
    油田合并
    函数学习
    Leetcode 103. Binary Tree Zigzag Level Order Traversal
    Leetcode 102. Binary Tree Level Order Traversal
    Leetcode 101. Symmetric Tree
    poj 2524 Ubiquitous Religions(宗教信仰)
    pat 1009. 说反话 (20)
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/11331780.html
Copyright © 2011-2022 走看看