雅礼集训 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\n", 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\n", 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\n", i, j);
		}
	}
  return 0;
}

线段游戏

题目描述:

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

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

题解:

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

posted @ 2018-11-27 13:03  ezhjw  阅读(233)  评论(1编辑  收藏  举报