• 雅礼集训 2017 Day2


    雅礼集训 2017 Day2

    水箱

    题目描述:

    给出一个长度为(n)宽度为1,高度无限的水箱,有个(n-1)挡板将其分为(n)(1 - 1)的小格,然后向每个小格中注水,水如果超过挡板就会溢出到挡板的另一边,这里的水是满足物理定律的(在无挡板阻拦的情况下会向低处流),现在有个条件 (m(i,y,k)),表示从左到右数的第(i)个格子中,在高度为(y+0.5)的地方是否有水,(k=1)表示有水,(k=0)表示没有水,请求出这(m)个条件最多能同时满足多少个条件。本题有多组数据。

    题解:

    这题妙啊,膜拜了1h题解。首先我们看一看(n=1)的情况,这个时候不用理挡板直接枚举水位即可,将所有条件按高度排序,每次决策是否放水即可。那么,考虑加入挡板,对于一个高度大于当前水位的挡板,可以直接将其视为无穷大,这时,这些挡板就将水箱划分成许多互不影响的子问题。那么每当水位淹过一个挡板时,就相当于将两个子问题合并,由于我们需要维护的东西十分少,所以可以直接加法合并。具体实现:记(f_x)表示当前区域内有多少条件要求一定有水,(g_x)表示当前区域的答案,转移:

    1、当前操作为“加入一个挡板”:那么就将左右两个连通块合并。

    2、当前操作为“有水”:那么可以将水放到当前水位,也可以不管(f_x ++,g_x = max(g_x,f_x))

    3、当前操作为“没水”:那么就直接累加进答案即可,(g_x++)

    !!!操作顺序十分重要!!!

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int maxn = 100000 + 10;
    
    namespace IO{
    
    const int S=(1<<20)+5;
    char buf[S],*H,*T;
    inline char Get()
    {
        if(H==T) T=(H=buf)+fread(buf,1,S,stdin);
        if(H==T) return -1;return *H++;
    }
    inline int rd()
    {
        int x=0;char c=Get();
        while(!isdigit(c)) c=Get();
        while(isdigit(c)) x=x*10+c-'0',c=Get();
        return x;
    }
    
    }
    
    int n, m, cnt;
    
    inline int cmp(int x, int y) {
      if(x == -1) return 1;
      if(y == -1) return 0;
      return x < y;
    }
    
    struct qnode{
      int kd, x, y;
      qnode() {}
      qnode(int _kd, int _x, int _y):kd(_kd), x(_x), y(_y) {}
      
      friend bool operator < (qnode a, qnode b) {
      	if(a.y != b.y) return a.y < b.y;
      	return cmp(a.kd,b.kd);
    	}
    }q[maxn << 1];
    
    //bcj
    int sfa[maxn << 1], f[maxn << 1], g[maxn << 1];
    
    int findfa(int u) { if(sfa[u] == u) return u; else return sfa[u] = findfa(sfa[u]); }
    
    inline void solve() {
    	using IO::rd;
    //  scanf("%d%d", &n, &m);
    	cnt = 0; n = rd(); m = rd();
      for(int i = 1;i < n;i ++) {
      	int x; x = rd();
    //		scanf("%d", &x);
        q[++ cnt] = qnode(-1,i,x);
    	}
      for(int i = 1;i <= m;i ++) {
      	int op, x, y;
      	x = rd(); y = rd(); op = rd();
    //  	scanf("%d%d%d", &x, &y, &op);
      	q[++ cnt] = qnode(op,x,y);
      }
      sort(q + 1,q + cnt + 1);
      for(int i = 1;i <= n + 1;i ++) sfa[i] = i;
      memset(f,0,sizeof(f));
      memset(g,0,sizeof(g));
    	int Ans = 0;
      for(int i = 1;i <= cnt;i ++) {
        int kd = q[i].kd;
        if(kd == -1) {
          int f1 = findfa(q[i].x);
          int f2 = findfa(q[i].x + 1);
          sfa[f2] = f1;
          f[f1] += f[f2];
          g[f1] += g[f2];
          Ans = max(Ans,g[f1]);
    		}
    		if(kd == 0) {
    		  int f1 = findfa(q[i].x);
    		  g[f1] ++;
    		  Ans = max(Ans,g[f1]);
    		}
    		if(kd == 1) {
    		  int f1 = findfa(q[i].x);
    		  f[f1] ++;
    		  g[f1] = max(g[f1],f[f1]);
    		  Ans = max(Ans,g[f1]);
    		}
    	}
    	printf("%d
    ", Ans);
    }
    
    int main() {
    	int cas; //scanf("%d", &cas);
    	cas = IO::rd();
    	while(cas --)
    	  solve();
      return 0;
    }
    

    棋盘游戏

    题目描述:

    给定一个棋盘,并有若干位置是障碍。然后Alice先钦定棋子的初始位置,接着从Bob开始轮流移动棋子,要求不能重复走,不能走障碍点,谁无法移动则输掉游戏,请统计所有Alice必胜的点,并输出。

    题解:

    首先无脑黑白染色一下,然后我们就得到了一个二分图。那么考虑必胜策略:

    1、如果存在完美匹配,Alice必败。因为每次Bob只需要走匹配边,那么Alice只能走非匹配边,因为存在完美匹配,所有Bob总有匹配边可以走,但是Alice就比较凉了~

    2、如果不存在完美匹配,那么我们思考一下奇偶性,如果一个点不一定在最大匹配上,那么它所在的所有交错轨长度应该都是偶数,那么Alice就比胜了。

    所以问题就变成如何求二分图中那些点不一定在最大匹配上。怎么求呢,我们可以先求出任意一个最大匹配,然后对每一个未匹配点找交错轨,如果找到了交错轨,那么它就不一定在最大匹配上。

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int maxn = 100 + 5;
    const int N = 10100;
    const int E = 200000 + 10;
    const int xx[4] = {-1,0,1,0};
    const int yy[4] = {0,1,0,-1};
    const int INF = 0x3f3f3f3f;
    
    int n, m, tot, id[maxn][maxn], flow;
    int h[N], ecnt, S, T;
    struct enode{
      int v, n, w, op;
      enode() {}
      enode(int _v, int _n, int _w, int _op):v(_v), n(_n), w(_w), op(_op) {}
    }e[E << 1];
    char s[maxn][maxn];
    
    inline void addedge(int u, int v, int w) {
    //	cout << u << ' ' << v << endl;
      ecnt ++; e[ecnt] = enode(v,h[u],w,ecnt + 1); h[u] = ecnt;
      ecnt ++; e[ecnt] = enode(u,h[v],0,ecnt - 1); h[v] = ecnt;
    }
    
    int level[N], q[E], cur[N];
    
    inline int bfs() {
      int head = 1, tail = 1;
      memset(level,0,sizeof(level));
      q[tail] = S;
      level[S] = 1;
      while(head <= tail) {
        int u = q[head]; head ++;
        for(int i = h[u];~ i;i = e[i].n) {
          int v = e[i].v;
          if(!level[v] && e[i].w > 0) {
          	level[v] = level[u] + 1;
          	q[++ tail] = v;
          	if(v == T) return 1;
          }
    		}
    	}
    	return 0;
    }
    
    int dfs(int u, int c) {
      if(u == T || c == 0) return c;
      int tmp = 0;
      for(int i = cur[u];~ i;i = e[i].n) {
        cur[u] = i;
        int v = e[i].v;
        if(level[v] == level[u] + 1) {
          int x = dfs(v,min(c,e[i].w));
          if(x) {
            tmp += x; c -= x;
            e[i].w -= x; e[e[i].op].w += x;
            if(!c) break;
    			}
    		}
    	}
    	if(!tmp) level[u] = -1;
    	return tmp;
    }
    
    inline void dicnic() {
    	flow = 0;
      while(bfs()) {
        for(int i = S;i <= T;i ++) cur[i] = h[i];
        flow += dfs(S,INF);
    	}
    }
    
    int vis[N];
    void dfs1(int u) {
      vis[u] |= 1;
      for(int i = h[u];~ i;i = e[i].n) {
        int v = e[i].v;
        if(!e[i].w || (vis[v] & 1)) continue;
        dfs1(v);
    	}
    }
    
    void dfs2(int u) {
      vis[u] |= 2;
      for(int i = h[u];~ i;i = e[i].n) {
        int v = e[i].v;
        if(!e[e[i].op].w || (vis[v] & 2)) continue;
        dfs2(v);
    	}
    }
    
    int main() {
    //	freopen("data.in","r",stdin);
    	memset(h,-1,sizeof(h));
    	memset(vis,0,sizeof(vis));
    	scanf("%d%d", &n, &m); tot = 0;
    	for(int i = 1;i <= n;i ++) scanf("%s", s[i] + 1);
    	for(int i = 1;i <= n;i ++) {
    	  for(int j = 1;j <= m;j ++) {
    	    if(s[i][j] == '#') continue;
    	    id[i][j] = ++ tot;
    		}
    	}
    	S = 0; T = tot + 1;
    	for(int i = 1;i <= n;i ++) {
    	  for(int j = 1;j <= m;j ++) {
    	  	if(s[i][j] == '#') continue;
    	  	if(!((i + j) & 1)) addedge(S,id[i][j],1);
    	  	else addedge(id[i][j],T,1);
    	  }
    	}
    	for(int i = 1;i <= n;i ++) {
    	  for(int j = 1;j <= m;j ++) {
    	    if((i + j) & 1) continue;
    	    if(s[i][j] == '#') continue;
    	    for(int k = 0;k < 4;k ++) {
    	    	int dx = i + xx[k];
    	    	int dy = j + yy[k];
    	    	if(dx < 1 || dy < 1 || dx > n || dy > m) continue;
    	    	if(s[dx][dy] == '#') continue;
    	    	addedge(id[i][j],id[dx][dy],1);
    	    }
    		}
    	}
    	dicnic();
    	dfs1(S);
    	dfs2(T);
    	int Ans = 0;
    	for(int i = 1;i <= n;i ++) {
    	  for(int j = 1;j <= m;j ++) {
    	  	if(s[i][j] == '#') continue;
    	    int x = id[i][j], k;
    	    if((i + j) & 1) k = 2;
    	    else k = 1;
    	    if(vis[x] == k) Ans ++;
    		}
    	}
    	printf("%d
    ", Ans);
    	for(int i = 1;i <= n;i ++) {
    	  for(int j = 1;j <= m;j ++) {
    	  	if(s[i][j] == '#') continue;
    	    int x = id[i][j], k;
    	    if((i + j) & 1) k = 2;
    	    else k = 1;
    	    if(vis[x] == k) printf("%d %d
    ", i, j);
    		}
    	}
      return 0;
    }
    

    线段游戏

    题目描述:

    给出若干条线段,要求支持两个操作:
    1、加入一条线段

    2、询问与(x = k)的交点最高的线段是哪条,如果有多条最高的就输出编号最小的那个。

    题解:

    好吧,这玩意儿是个模板题,我好菜啊。所以这玩意儿就是个裸的李超线段树,,,

  • 相关阅读:
    AC自动机算法与AC自动机专辑
    hdu 2757 DNA repair AC自动机dp完全不懂
    hdu 3695 AC自动机模板题
    Magic Tree 水dp 错题!
    AC自动机模板
    UVa 10003 Cutting Sticks 区间dp
    hdu 3695 Computer Virus on Planet Pandora AC自动机
    [置顶] AC自动机算法与AC自动机专辑
    Trip 图dp
    利用数据库创建webservice
  • 原文地址:https://www.cnblogs.com/ezhjw/p/10025774.html
Copyright © 2020-2023  润新知