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
  • 相关阅读:
    风讯DotNetCMS sp5安装笔记
    datalist中实现自动编号写法
    windows server 2008 自动登录设置
    C#软件监控外部程序运行状态
    WPF启动屏幕SplashScreen
    Windows Server 2019 在桌面上显示“我的电脑”
    使用jquery的load方法加载html页面,html引入的js无效
    sql查询文章上一篇下一篇
    C#调用user32.dll Win32的API函数
    C#调用dll提示"试图加载格式不正确的程序
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/11331780.html
Copyright © 2011-2022 走看看