提高十连测day3

提高十连测day3

A

我们可以枚举两个 $ 1 $ 之间的相隔距离,然后计算形如 $ 00100100 \cdots $ 的串在原串中最⻓⼦序列匹配即可,复杂度 $ O(n^2) $ 。寻找 $ S $ 在 $ T $ 中的最⻓⼦序列匹配直接贪⼼的扫⼀遍就⾏了。
我们可以考虑优化这个过程,快速匹配连续的 $ 0 $ 。只要⼆分找出下⼀个 的匹配位置即可。
由于 $ 1 $ 的个数为调和级数 ,所以总复杂度 $ O(n \log n) $ 。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std; 

#define LL long long
const int N = 1e5 + 100; 

int cnt,len,ans;
int sum[N],num[N];
char ch[N]; 

inline int Find(int pos, int x) {
	int l = pos, r = cnt;
    int pre = cnt + 1; 
	while(l <= r) {
		int mid = (l + r) >> 1; 
		if(sum[mid] >= x) {
			pre = mid; 
			r = mid-1; 
		}
		else l = mid + 1; 
	}
	return pre; 
}
inline void check(int x) {
	int key = x, pos = 1, pre = 0; 
	while(true) {
		pos = Find(pos, key); 
		if(pos == cnt) {
			if(pre + x >= x * 2 + 1)
                ans = max(pre + x, ans); 
			return; 
		}
		if(pos == cnt + 1) {
			if(pre >= x * 2 + 1)
			    ans = max(pre, ans); 
			return; 
		}
		key = sum[pos] + x; 
		pre += x + 1; 
	}
	return; 
}

int main() {
	scanf("%s", ch + 1); 
	len = strlen(ch + 1); 
	for(int i = 1 ; i <= len ; i++)
	    num[i] = ch[i] - '0'; 
	num[++len] = 1; 
	int tmp = 0; 
	for(int i = 1 ; i <= len - 1 ; i++)
	if(!num[i]) tmp = 0; 
	else {
		tmp++; 
		ans = max(tmp, ans); 
	}
	int now = 0; 
	for(int i = 1 ; i <= len ; i++) {
		if(!num[i]) now++; 
		else sum[++cnt] = now; 
	}
	for(int i = 1 ; i <= len ; i++) check(i); 
	printf("%d \n", ans); 
    //system("pause");
	return 0; 
}

B

我们⾸先可以想到朴素地将到达每个点的所有距离⽤背包记下来,可以获得 $ 40 $ 分,位运算优化背包可以获得 $ 60 $ 分。
但其实可以用 $ set $ 维护合法的路径,可以得 $ 60 $ 分。
接下来考虑我们并不需要到达⼀个点的所有距离。
我们考虑三个距离 $ x,y,z $ ,若 ,$ \frac{1}{11}z \leq x \leq y \leq z $ 那么 $ y $ 这个距离可以不⽤记。那么每个点只需要记录 $ \log $ 级别的距离。合并两个距离集合 $ A,B $ 时使⽤归并排序可以做到 $ O(\los M) $ 。
最终复杂度为 $ O(m \log M) $ 。其中 $ M $ 是值域。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

#define LL long long
#define M 410
const int N = 2e5 + 100;

struct Edge {
	int to,from;
	int data;
}e[N * 2];
LL head[N],degree[N];
LL num[N],n,m,q,cnt;
LL dis[N][M],tmp[N];
queue<int> qu;

inline void add_edge(int x,int y,int z) {
	e[++cnt].from = y;
	e[cnt].data = z;
	e[cnt].to = head[x];
	head[x] = cnt;
	degree[y]++;
}
void Find(int x,int y,int val) {
	int l = 1,r = 1,now = 0;
	while(l <= num[x] && r <= num[y]) {
		LL u = dis[x][l] + val;
		if(u < dis[y][r]) {
			while(now >= 2 && u >= tmp[now] && (double)tmp[now - 1] > (double)u / 1.1) now--;
			tmp[++now] = u;
			l++; 
		} else {
			while(now >= 2 && dis[y][r] >= tmp[now] && (double)tmp[now - 1] > (double)dis[y][r] / 1.1) now--;	
			tmp[++now] = dis[y][r];
			r++;
		}
	}
	while(l <= num[x]) {
		LL u = dis[x][l] + val;
		while(now >= 2 && u >= tmp[now] && (double)tmp[now - 1] > (double)u / 1.1) now--;
		tmp[++now] = u;
		l++; 
	}
	while(r <= num[y]) {
		while(now >= 2 && dis[y][r] >= tmp[now] && (double)tmp[now - 1] > (double)dis[y][r] / 1.1) now--;	
		tmp[++now] = dis[y][r];
		r++;
	}
	num[y] = now;
	for(int i = 1 ; i <= now ; i++) 
		dis[y][i] = tmp[i];
}
inline void prework() {
	qu.push(1);
	dis[1][++num[1]] = 0;
	while(!qu.empty()) {
		int u = qu.front();
		qu.pop();
		for(int i = head[u] ; i ; i = e[i].to) {
			int v = e[i].from;
			Find(u,v,e[i].data);
			degree[v]--;
			if(!degree[v]) qu.push(v);
		}
	}	
}

int main() {
	scanf("%lld%lld%lld",&n,&m,&q);
	for(int i = 1 ; i <= m ; i++) {
		LL x,y,z;
		scanf("%lld%lld%lld",&x,&y,&z);
		add_edge(x,y,z);
	}
	prework();
	while(q--) {
		LL disx,x;
		scanf("%lld%lld",&x,&disx);
		int pos = lower_bound(dis[x] + 1,dis[x] + num[x] + 1,disx) - dis[x];
		if(pos == num[x] + 1) {
			puts("NO");
			continue;
		}
		if((double)dis[x][pos] <= (double)1.1 * disx) puts("YES");
		else puts("NO");
	}
	//system("pause");
	return 0;
}

C

不难发现对于⼀个询问,值⼩于等于某个值的所有位置可以由⼀些⾏和列的并表示出。联通分为⼏种情况。

  1. 井字型联通,即有⼀⾏通过第⼀个位置,有⼀列通过第⼆个位置或反过来。答案为曼哈顿距离。
  2. Z字型联通,即两个位置都有⾏通过,并且有⾄少⼀个列。若列在两个位置之间则答案就是曼哈顿距离,否则需要找到最靠近中间的列来计算距离。
  3. 两个位置之间的所有⾏或所有列都出现。答案为曼哈顿距离。

若我们对⾏和列维护⼀个数组,数组中的值为最后⼀次被清空的时间,那么对于⼀个询问,这个⾏或列在询问中出现当且仅当它的值⼤于等于某个数。我们只需要维护两个操作:找到在 $ x $ 位置后的第⼀个⼤于等于 $ v $ 的位置、找到在 $ x $ 位置前的最后⼀个⼤于等于 $ v $ 的位置。这两个操作可以⽤线段树上⼆分实现,时间复杂度 $ O(\log n) $ 。
对于第三种情况可能需要单独维护⼀个单点修改询问区间极值的线段树。
总复杂度 $ O(Q \log n) $ 。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

#define LL long long
#define lson x * 2
#define rson x * 2 + 1
const int N = 1e5 + 100; 

struct Segment_Tree {
	struct Tree {
		int tag, minn, maxx; 
	}tree[N << 2]; 
	void pushup(int x) {
		tree[x].minn = min(tree[lson].minn, tree[rson].minn); 
		tree[x].maxx = max(tree[lson].maxx, tree[rson].maxx); 
	}
	void build(int x, int l, int r) {
		if(l == r) {
			tree[x].minn = 0;
            tree[x].maxx = 0; 
			return; 
		}
		int mid = (l + r) >> 1; 
		build(lson, l, mid); 
		build(rson, mid + 1, r); 
		pushup(x); 
	}
	void pushdown(int x) {
		if(tree[x].tag) {
			tree[lson].minn += tree[x].tag; 
			tree[rson].minn += tree[x].tag; 
			tree[lson].maxx += tree[x].tag; 
			tree[rson].maxx += tree[x].tag; 
			tree[lson].tag += tree[x].tag; 
			tree[rson].tag += tree[x].tag; 
			tree[x].tag = 0; 
		}
	}
	void update(int x, int l, int r, int ll, int rr, int c) {
		if(l == ll && r == rr) {
			tree[x].tag += c; 
			tree[x].minn += c; 
			tree[x].maxx += c; 
			return; 
		}
		pushdown(x); 
		int mid = (l + r) >> 1; 
		if(rr <= mid) update(lson, l, mid, ll, rr, c); 
		else if (ll > mid) update(rson, mid + 1, r, ll, rr, c); 
		else {
			update(lson, l, mid, ll, mid, c); 
			update(rson, mid + 1, r, mid + 1, rr, c); 
		}
		pushup(x); 
	}
    void delet(int x, int l, int r, int v) {
		if(l == r) {
			tree[x].minn = 0;
            tree[x].maxx = 0; 
			return; 
		}
		pushdown(x); 
		int mid = (l + r) >> 1; 
		if(v <= mid) delet(lson, l, mid, v); 
		else delet(rson, mid + 1, r, v); 
		pushup(x); 
	}
	int query(int x, int l, int r, int ll, int rr, int v) {
		if(l == ll && r == rr) {
			if(v == 1) return tree[x].minn; 
			else return tree[x].maxx; 
		}
		pushdown(x); 
		int mid = (l + r) >> 1; 
		if(rr <= mid) return query(lson, l, mid, ll, rr, v); 
		else if(ll > mid) return query(rson, mid + 1, r, ll, rr, v); 
		else {
			if(v == 1) return min(query(lson, l, mid, ll, mid, v), query(rson, mid + 1, r, mid + 1, rr, v)); 
			else return max(query(lson, l, mid, ll, mid, v), query(rson, mid + 1, r, mid + 1, rr, v)); 
		}
	}
}line, row; 

int n, m, q; 

int find1(int x, int v) {
	int l = 1, r = x-1, res = -1; 
	while(l <= r) {
		int mid = (l + r) >> 1; 
		if(row.query(1, 1, m, mid, x-1, 1) <= v) {
			res = mid; 
			l = mid + 1; 
		}
		else r = mid-1; 
	}
	return res; 
}
int find2(int x, int v) {
	int l = x + 1, r = m, res = -1; 
	while(l <= r) {
		int mid = (l + r) >> 1; 
		if(row.query(1, 1, m, x + 1, mid, 1) <= v) {
			res = mid; 
			r = mid-1; 
		}
		else l = mid + 1; 
	}
	return res; 
}
int find3(int x, int v) {
	int l = 1, r = x-1, res = -1; 
	while(l <= r) {
		int mid = (l + r) >> 1; 
		if(line.query(1, 1, n, mid, x-1, 1) <= v) {
			res = mid; 
			l = mid + 1; 
		}
		else r = mid-1; 
	}
	return res; 
}
int find4(int x, int v) {
	int l = x + 1, r = n, res = -1; 
	while(l <= r) {
		int mid = (l + r) >> 1; 
		if(line.query(1, 1, n, x + 1, mid, 1) <= v) {
			res = mid; 
			r = mid-1; 
		}
		else l = mid + 1; 
	}
	return res; 
}
bool deal1(int a, int b, int c, int d, int v) {
	if(a > c) swap(a, c); 
	if(line.query(1, 1, n, a, c, 2) <= v) return 1; 
	if(b > d) swap(b, d); 
	if(row.query(1, 1, m, b, d, 2) <= v) return 1; 
	return 0; 
}
bool deal2(int a, int b, int c, int d, int v) {
	int av = line.query(1, 1, n, a, a, 1);
    int bv = row.query(1, 1, m, b, b, 1);
    int cv = line.query(1, 1, n, c, c, 1);
    int dv = row.query(1, 1, m, d, d, 1); 
	if(a == c && av <= v) return 1; 
	if(b == d && bv <= v) return 1; 
	if(av <= v && dv <= v) return 1; 
	if(bv <= v && cv <= v) return 1; 
	if(av <= v && cv <= v && row.query(1, 1, m, min(b, d), max(b, d), 1) <= v) return 1; 
	if(bv <= v && dv <= v && line.query(1, 1, n, min(a, c), max(a, c), 1) <= v) return 1; 
	return 0; 
}
int deal3(int a, int b, int c, int d, int v) {
	int res, mins = 2e9, dis = abs(a - c) + abs(b - d); 
	if(line.query(1, 1, n, a, a, 2) <= v && line.query(1, 1, n, c, c, 2) <= v) {
		res = find1(min(b, d), v); 
		if(res != -1) mins = min(mins, dis + 2 * abs(res - min(b, d))); 
		res = find2(max(b, d), v); 
		if(res != -1) mins = min(mins, dis + 2 * abs(res - max(b, d))); 
	}
	if(row.query(1, 1, n, b, b, 2) <= v && row.query(1, 1, n, d, d, 2) <= v) {
		res = find3(min(a, c), v); 
		if(res != -1) mins = min(mins, dis + 2 * abs(res - min(a, c))); 
		res = find4(max(a, c), v); 
		if(res != -1) mins = min(mins, dis + 2 * abs(res - max(a, c))); 
	}
	if(mins == 2e9) return-1; 
	else return mins; 
}
int work() {
    int a,b,c,d,v;
    scanf("%d%d%d%d%d",&a,&b,&c,&d,&v);
	if(a == c && b == d) return 0; 
	if(deal1(a, b, c, d, v) || deal2(a, b, c, d, v))
        return abs(a - c) + abs(b - d); 
	return deal3(a, b, c, d, v); 
}

int main() {
	scanf("%d%d%d",&n,&m,&q);
	line.build(1, 1, n); 
	row.build(1, 1, m); 
	while (q--) {
		line.update(1, 1, n, 1, n, 1); 
		row.update(1, 1, m, 1, m, 1); 
		int opt,x;;
        scanf("%d",&opt); 
		if(opt == 1) {
            scanf("%d",&x);
			line.delet(1, 1, n, x); 
		}
		if(opt == 2) {
            scanf("%d",&x);
			row.delet(1, 1, m, x); 
		}
		if(opt == 3) printf("%d \n",work());
	}
    //system("pause");
	return 0; 
}
posted @ 2019-09-18 20:58  西窗夜雨  阅读(158)  评论(0编辑  收藏  举报