zoukankan      html  css  js  c++  java
  • 18.10.7 考试总结

     

    这道题还是很温柔的签到题 看了两分钟就搞出来了 然后忘记在每次更新队首之后重新更新答案了

    然后幸好改出来惹 

    用滑动窗口维护对于每个右端点的最优左端点 就是每次加入元素 如果合法那么我就贪心的弹出队头然后区间长度取最小值即可

    代码

    #include <bits/stdc++.h>
    #define oo 1e9
    using namespace std;
    
    const int N = 2 * 1e5 + 5;
    int T, a[N], n, k, r, b, q, cmd[N], num, len;
    int t[N];
    
    void clear( ) {
        memset(t, 0, sizeof(t));
        memset(cmd, 0, sizeof(cmd));
        num = 0;
    }
    
    bool check(int pos) {
        
        if(t[a[pos]] - 1 >= cmd[a[pos]]) return true;
        return false;
    }
    
    void deal( ) {
        
        int tot = 0; int head = 0, tail = 0, las = 1;
        len = oo;
        for(int i = 1;i <= n;i ++) {
            tail ++; t[a[i]] ++;
            if(t[a[i]] == cmd[a[i]]) tot ++;
            while(check(las) && head < tail) {
                t[a[las]] -- ;
                las ++, head ++;
            }
            if(tot == num) len = min(len, i - las + 1);
        }
    }
    
    int read( ) {
        
        int t = 1, ans = 0;
        char x; x = getchar( );
        while(x < '0' || x > '9') {
            if(x == '-') t = -1;
            x = getchar( );
        }
        while(x >= '0' && x <= '9') {
            ans = ans * 10 + x - '0';
            x = getchar( );
        }
        return ans * t;
    }
    
    void Solve( ) {
        
        scanf("%d",& T);
        while(T --) {
            clear( );
            n = read( ), k = read( ), r = read( );
            for(int i = 1;i <= n;i ++) a[i] = read( );
            for(int i = 1;i <= r;i ++) {
                b = read( ); q = read( );
                cmd[b] = max(cmd[b], q);
            }
            for(int i = 0;i <= k;i ++) 
                if(cmd[i] > 0) num ++;
            deal( );
            if(len == oo) printf("DESTROY ALL
    ");
            else printf("%d
    ", len);
        }
    }
    
    int main( ) {
        
        freopen("drop.in","r",stdin);
        freopen("drop.out","w",stdout);
        Solve( );
    }

       

    emmmmm考试的时候wans给我说这道题是2sat我还不信... 然后就被华丽丽的打脸饿

    先复习一下这玩意儿吧 这个玩意儿是对于很多约束条件进行建边 $a -> b$表示选择了$a$就必须选择$b$

    同一种东西有两种状态$a, -a$ 显然这两种状态是不能同时出现的 也就是说如果我沿着一种状态可以走到另一状态 那么这个条件就是不合法的

    并且普通的$2sat$具有对称性 比如某个条件$(i, j)$表示$i, j$ 不能共存 也就是说连边$i -> -j,j -> -i$

    所以对于2sat而言能从$i$走到$j$就一定能从$j$走到$i$ 也就是说他们在同一个强连通分量中 跑一遍$tarjan$ 在判断每个点的两个状态是否在同一个强连通分量就好了

    那么对于这道题而言 题目条件$(i, j)$建边方式和上面的一样 所以也就是说这样子建图是尽可能保证不出现行星被炸掉的情况

    所以我们的目的就转化成了添加若干条边 使某物品的两状态可以互相到达 由于对称性 我们只需要保证一个点能走到另一个点就可以了

    假设上面的点表示正状态 下面负状态 那么我们建边只能从上面的点指向下面的点

    对于这种上面的状态可以指向下面的状态而言 因为对称性 所以只要下面的点能够到上面 根据对称性 就只需要添加一条虚线边即可 直接到达自己显然不需要添加任何边

    那么相反的 就只需要多添加自己的上面指向下面那条边就可以了 

    那么无解的情况就是下面的点没有一个能够到上面 也就是说走不通路的情况了 因为我们只能从上往下建边

    枚举每个点 在这样子讨论一下跑跑dfs就可以了

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 4000 + 5;
    int T, n, m, tot, head[N], nex[2 * N],tov[2 * N];
    bool vis[N];
    
    void add(int u, int v) {
        
        tot ++;
        nex[tot] = head[u];
        tov[tot] = v;
        head[u] = tot;
    }
    
    int read( ) {
        
        int t = 1, ans = 0;
        char x; x = getchar( );
        while(x < '0' || x > '9') {
            if(x == '-') t = -1;
            x = getchar( );
        }
        while(x >= '0' && x <= '9') {
            ans = ans * 10 + x - '0';
            x = getchar( );
        }
        return t * ans;
    }
    
    void Add_Edge( ) {
        
        n = read( ),m = read( );
        for(int i = 1;i <= m;i ++) {
            int x, y;
            x = read( ), y = read( );
            int xx = x > 0 ? x + n : -x;
            int yy = y > 0 ? y + n : -y;
            if(x < 0) x = -x + n;
            if(y < 0) y = -y + n;
            add(x, yy); add(y, xx);
        }
    }
    
    void clear( ) {
        
        memset(head, 0, sizeof(head));
        tot = 0;
    }
    
    void dfs(int u) {
        
        vis[u] = true;
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(! vis[v]) dfs(v);
        }
    }
    
    void Solve( ) {
        
        scanf("%d",& T);
        while(T --) {
            clear( );
            Add_Edge( );
            int ans = 3;
            for(int i = 1;i <= n;i ++) {
                memset(vis, 0, sizeof(vis));
                dfs(i);
                int ret = 3;
                if(vis[i + n]) {
                    memset(vis, 0, sizeof(vis));
                    dfs(i + n); if(vis[i]) {ans = 0; continue;}
                    for(int j = 1;j <= n;j ++) if(vis[j]) {
                        ret = 1; break;
                    }
                }
                else {
                    memset(vis, 0, sizeof(vis));
                    dfs(i + n); if(vis[i]) {ans = min(ans, 1); continue;}
                    for(int j = 1;j <= n;j ++) if(vis[j]) {
                        ret = 2; break;
                    }
                }
                ans = min(ans, ret);    
            }
            if(ans == 3) printf("No Way
    ");
            else printf("%d
    ",ans);
        }
    }
    
    int main( ) {
    
        freopen("god.in","r",stdin);
        freopen("god.out","w",stdout);
        Solve( );
    }

      

     

    考试的时候$n$方暴力真的美滋滋还有60分

    正解的思路其实和暴力是一样的 但是他用了无比优美的优化

    暴力的思想大概是 贪心

    先从左往右扫,如果某一时刻不满足要求,则需要删除前面中某一个支持对方的人。我们贪心地选择删除当前时刻访问的人(肯定是支持对方),然后继续往后扫。

    然后再从右往左扫,作相同的操作。

    直观地理解是这样的:我们尽量删除靠右的人,使得从右往左扫时少删除一些人。

    可以采用交换论证法证明这贪心是对的。

    我们可以把$C$看作$-1$,$V$看作$1$ 那么对于一段从右向左的区间而言 最小后缀和相反数就能表示杀掉人的个数 因为$C$越多 后缀和越小

    题解写得好 我复制一波 在按照自己的理解修改一下

    可以发现一个区间从左往右扫完,从右往左扫的这个过程可以不用实现出来。只需要求出剩下节点中从右端点开始的最小后缀和的相反数即可。

    然后我们发现,如果两个询问区间拥有相同的左端点,则只需要作一次从左往右扫的工作。这使我们想到要离线化解决问题。

    我们将询问按左端点排序,按照左端点从大到小的顺序求解询问。 至于每个区间的最小后缀和很容系想到用线段树维护

    如果已知从$i$开始向右扫需要删除那些结点,则从$i - 1$开始向右扫需要删除那些结点可以快速求出 在进行修改的同时进行线段树的维护。

    具体来说 如果$i-1$是支持者 则左数第一个被删除的结点与它抵消 相当于把他放了出来 也就是说他会在后续的后缀和中做出$-1$的贡献 注意后缀和指的是剩下的人和

    如果$i - 1$是反对者 则加入被删除的结点里 他对线段树不作出贡献。

    该过程可以用栈维护。

    那么对于每个询问 可以在栈中二分得到从左往右的要杀的人的个数 在线段树中求最小后缀和 取相反数相加即可

    总的来说 栈维护从左往右杀的人 线段树维护剩下的人从右往左杀的人

    代码(这道题我竟然没看std改出来了hhh)

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 5 * 1e5 + 5;
    int a[N], q, sum[4 * N], f[4 * N], stk[N], top, n, ans[N];
    char s[N];
    struct event {
        
        int l, r, id;
        event(int l = 0, int r = 0, int id = 0): l(l), r(r), id(id) {}
    }b[N];
    
    bool cmp(const event & a, const event & b) {
        return a.l < b.l;
    }
    
    void Init( ) {
        
        scanf("%d",& n);
        scanf("%s",s + 1);
        for(int i = 1;i <= n;i ++) a[i] = s[i] == 'C' ? 1 : -1;
        scanf("%d",& q);
        for(int i = 1;i <= q;i ++) {
            int l,r;
            scanf("%d%d",& l,& r);
            b[i] = event(l, r, i);
        }
        sort(b + 1, b + q + 1, cmp);
    }
    
    void update(int o) {
        
        sum[o] = sum[2 * o] + sum[2 * o + 1];
        f[o] = min(f[2 * o + 1], sum[2 * o + 1] + f[2 * o]);
    }
    
    void modify(int o, int l, int r, int pos, int del) {
        
        if(l == r) {
            f[o] += del;
            sum[o] += del;
            return ;
        }
        int mid = l + r >> 1;
        if(pos <= mid) modify(2 * o, l, mid, pos, del);
        else modify(2 * o + 1, mid + 1, r, pos, del);
        update(o);
    }
    
    void init(int pos) {
        
        if(a[pos] == 1) {
            stk[++ top] = pos;
            //modify(1, 1, n, pos, -1);
        }
        else {
            modify(1, 1, n, pos, 1);
            if(top > 0) {
                modify(1, 1, n, stk[top --], -1);
            }
        }
    }
    
    bool check(int pos, int side) {
        
        return stk[pos] <= side;
    }
    
    int find_pos(int pos) {
        
        int l = 1,r = top,ans = top + 1;
        bool tag = false;
        while(l <= r) {
            int mid = l + r >> 1;
            if(check(mid, pos)) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        return ans;
    }
    
    event query(int o, int l, int r, int L, int R) {
        
        if(l >= L && r <= R) {
            return event(f[o], sum[o], 0);
        }
        int mid = l + r >> 1;
        if(R <= mid) return query(2 * o, l, mid, L, R);
        if(L > mid)  return query(2 * o + 1, mid + 1, r, L, R);
        event q1 = query(2 * o, l, mid, L, R);
        event q2 = query(2 * o + 1, mid + 1, r, L, R);
        event q3; 
        q3.l = min(q1.l + q2.r, q2.l); q3.r = q2.r + q1.r;
        return q3;
    }
    
    void Solve( ) {
        
        int now = q;
        for(int i = n;i >= 1;i --) {
            init(i);event aa = query(1, 1, n, 1, 11);
            while(i == b[now].l) {
                
                int num1 = find_pos(b[now].r); 
                num1 = top - num1 + 1;
                event num2 = query(1, 1, n, b[now].l, b[now].r); 
                int as = num1 + max(0, -num2.l);
                ans[b[now].id] = as; now --;
            }
        }
        for(int i = 1;i <= q;i ++) printf("%d
    ",ans[i]);
    }
    
    int main( ) {
        
        freopen("sworder.in","r",stdin);
        freopen("sworder.out","w",stdout);
        Init( );
        Solve( );
        return 0;
    }
  • 相关阅读:
    Linux-3.14.12内存管理笔记【构建内存管理框架(3)】
    Linux-3.14.12内存管理笔记【构建内存管理框架(2)】
    Linux-3.14.12内存管理笔记【构建内存管理框架(1)】
    Linux-3.14.12内存管理笔记【建立内核页表(3)
    Linux power supply class(1)_软件架构及API汇整【转】
    Linux-3.14.12内存管理笔记【建立内核页表(2)】-低端内存的建立
    Linux-3.14.12内存管理笔记【建立内核页表(1)】
    linux防火墙相关 iptables
    shell 备份 source code
    crontab清理日志
  • 原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9751082.html
Copyright © 2011-2022 走看看