2022NOIP A层联测26

A. 乘筛积

\(exgcd\) 不能乱改

直接 \(exgcd\) 优化找 \(x\)+记忆化能过,但是因为脑残加了个自认为的剪枝于是挂惨了

题解做法是根号分治

exgcd
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}


int exgcd(int a, int b, int &x, int &y){
	if(!b){x = 1; y = 0; return a;}
	int gcd = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return gcd;
}
const int maxn = 300005;
const int mod = 998244353;
int n, m, c;
int a[maxn], b[maxn];
typedef pair<int, int> pii;
map<pair<int, int>, int>mp;
void sol(){
	int p = read(), q = read();
	if(mp[pii(p, q)]){
		printf("%d\n",mp[pii(p, q)]);
		return;
	}
	int x, y;
	int gcd = exgcd(p, q, x, y);
	if(c % gcd){
		printf("%d\n",0);
		return;
	}
	int ans = 0;
	int mq = q / gcd;
	x = (1ll * x * c / gcd) % mq;
	x = (x + mq) % mq;
	for(int i = x; i <= n; i += mq){
		int j = c - i * p;
		if(j < q)break;
		if(j % q)continue;
		j /= q;
		if(j > m)continue;
		ans = (ans + 1ll * a[i] * b[j] % mod) % mod;
	}
	mp[pii(p, q)] = ans;
	printf("%d\n",ans);
}
int main(){
	freopen("sedge.in","r",stdin);
	freopen("sedge.out","w",stdout);
	n = read(), m = read(), c = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= m; ++i)b[i] = read();
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

根号分治
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 300005;
const int mod = 998244353;
int n, m, c, mx;
int a[maxn], b[maxn];
typedef pair<int, int> pii;
map<pair<int, int>, int>mp;
void sol(){
	int p = read(), q = read();
	if(c % __gcd(p, q)){printf("0\n");return;}
	if(mp[pii(p, q)]){printf("%d\n",mp[pii(p, q)]);return;}
	int ans = 0;
	if(p > q){
		for(int i = max(1ll, (c - 1ll * m * q) / p); i <= n; ++i){
			int j = c - i * p; if(j < q)break;
			if(j % q)continue; j /= q;
			if(j <= m) ans = (ans + 1ll * a[i] * b[j] % mod) % mod;
		}
	}else{
		for(int i = max(1ll, (c - 1ll * n * p) / q); i <= m; ++i){
			int j = c - i * q; if(j < p)break;
			if(j % p)continue; j /= p;
			if(j <= n) ans = (ans + 1ll * a[j] * b[i] % mod) % mod;
		}
	}
	mp[pii(p, q)] = ans;
	printf("%d\n",ans);
}
int main(){
	// freopen("sedge.in","r",stdin);
	// freopen("sedge.out","w",stdout);
	n = read(), m = read(), c = read();
	mx = sqrt(c);
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= m; ++i)b[i] = read();
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

B. 放进去

一堆假贪心过的。。。

\(SOS\) \(DP\) 即子集和 \(DP\)

求解形如 \(f_i = \sum_{i | j = i} g_j\)

本质上与 \(fwt\) 相同, 也可以看做高维前缀和

其实是优化了暴力, \(f_s\) 表示选择集合为 \(s\) 的最小代价,直接转移复杂度很高,考虑优化

对每个物品分别考虑

选择一个集合,贡献是其中对应最小的 \(a\),集合状态该位不为 \(0\),那么贡献都是 \(a\), 如果为 \(0\) 则有新的贡献

我们用 \(SOS\) 进行子集上的差分,具体来讲按照 \(a\) 降序处理,设当前的前缀状态集合为 \(s\), 那么\(f_s += a_i\)

不含新加入的这一位的贡献不变于是 \(f_{las} -= a_i\)

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 34000000;
int n, m, a[30];
ll sb[maxn], f[maxn];
int lg[maxn];
int p[30], b[30];
bool cmp(int x, int y){return a[x] < a[y];}
int main(){
	freopen("putin.in","r",stdin);
	freopen("putin.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; ++i)p[i] = i - 1;
	for(int i = 1; i <= n; ++i){
		for(int j = 0; j < m; ++j)a[j] = read();
		sort(p + 1, p + m + 1, cmp);
		for(int s = 0, j = m; j > 0; --j){
			f[s] -= a[p[j]]; s |= (1 << p[j]); f[s] += a[p[j]];
		}
	}
	for(int i = 0; i < m; ++i)
		for(int j = 0; j < (1 << m); j += (1 << (i + 1)))
			for(int k = 0; k < (1 << i); ++k)
				f[j + k] += f[j + k + (1 << i)];
	for(int i = 1; i <= m; ++i)b[i] = read();
	for(int i = 2; i < (1 << m); i += i)lg[i] = lg[i >> 1] + 1;
	for(int i = 1; i < (1 << m); ++i)sb[i] = sb[i ^ (i & -i)] + b[lg[i & -i] + 1];
	ll ans = 4e18;
	for(int s = 1; s < (1 << m); ++s)ans = min(ans, sb[s] + f[s]);
	printf("%lld\n",ans);
	return 0;
}

C. 最长路径

考虑一个点能由谁转移过来,转移的点满足 \(x_i + 1 == x _j\) 或者 \(y_i + 1 == y_j\) 否则一定不是最优

暴力枚举每个包含当前点的矩形,找到能贡献的最小 \(x, y\)进行转移

然后优化这个过程,首先优化找 \(x, y\)

考虑一个矩形会贡献到的是一个矩形范围,我们在矩形下边界处放最小值,最后取后缀 \(min\)

按照最小值排序,这样一个点只会覆盖一次于是可以并查集优化找点过程

而对应的转移优化,则是发现后缀 \(min\) 有单调性,于是用单调队列优化

为了方便查方案数,每个单调队列配套一个桶,记录 \(f = i\)时队列中元素的方案和

目前 \(TLEcoders\)\(TLE\) 70,有大佬会卡常吗

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 2055;
const int mod = 1e9 + 7;
int n, m, q;
struct matrix{
	int r1, c1, r2, c2;
}d[520005];
struct DSU{
	int f[maxn];
	void init(int x){for(int i = 1; i <= x; ++i)f[i] = i;}
	int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
	void merge(int x, int y){x = fa(x); y = fa(y); if(x < y)swap(x, y); f[y] = x;}
}s[maxn];
int p[520005];
int f[maxn][maxn], g[maxn][maxn];
void add(int &x, int y){x += y; x = x >= mod ? x - mod : x;}
void Ckmi(int &x, int y){x = x > y ? y : x;}

struct Qr{
	int q[maxn], head, tail, i, sg[maxn];
	void init(int _i){head = 1; tail = 0; i = _i;}
	void push(int v){
		while(head <= tail && f[i][q[tail]] < f[i][v]){
			add(sg[f[i][q[tail]]], mod - g[i][q[tail]]);
			--tail;
		}
		q[++tail] = v; add(sg[f[i][v]], g[i][v]);
	}
	void pop(int lim){
		while(head <= tail && q[head] < lim){
			add(sg[f[i][q[head]]], mod - g[i][q[head]]);
			++head;
		}
	}
	int qf(int lim){
		pop(lim);
		if(head <= tail)return f[i][q[head]];
		else return -0x3f;
	}
	int qg(){return sg[f[i][q[head]]];}
	void clear(){pop(0x3f3f3f3f);}
}qr[maxn];

struct Qu{
	int q[maxn], head, tail, i, sg[maxn];
	void init(int _i){head = 1; tail = 0; i = _i;}
	void push(int v){
		while(head <= tail && f[q[tail]][i] < f[v][i]){
			add(sg[f[q[tail]][i]], mod - g[q[tail]][i]);
			--tail;
		}
		q[++tail] = v; add(sg[f[v][i]], g[v][i]);
	}
	void pop(int lim){
		while(head <= tail && q[head] < lim){
			add(sg[f[q[head]][i]], mod - g[q[head]][i]);
			++head;
		}
	}
	int qf(int lim){
		pop(lim);
		if(head <= tail)return f[q[head]][i];
		else return -0x3f;
	}
	int qg(){return sg[f[q[head]][i]];}
	void clear(){pop(0x3f3f3f3f);}
}qu[maxn];

int mil[maxn][maxn], miu[maxn][maxn];
bool cmp1(int x, int y){return d[x].r1 < d[y].r1;}
bool cmp2(int x, int y){return d[x].c1 < d[y].c1;}
void sol(){
	n = read(), m = read(), q = read();
	for(int i = 1; i <= q; ++i)d[i].r1 = read(), d[i].c1 = read(), d[i].r2 = read(), d[i].c2 = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			f[i][j] = g[i][j] = 1;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			mil[i][j] = j, miu[i][j] = i;
	for(int i = 1; i <= q; ++i)p[i] = i; sort(p + 1, p + q + 1, cmp1);
	for(int i = 1; i <= n; ++i)s[i].init(m);
	for(int i = 1; i <= q; ++i){
		int now = p[i], R = d[now].r2;
		if(d[now].r2 > d[now].r1){
			for(int j = d[now].c1 + 1; j <= d[now].c2; j = s[R].fa(j) + 1)Ckmi(miu[d[now].r2][j], d[now].r1);
			for(int j = d[now].c1 + 1; j < d[now].c2; j = s[R].fa(j))s[R].merge(j, j + 1);
		}
	} 
	sort(p + 1, p + q + 1, cmp2);
	for(int i = 1; i <= m; ++i)s[i].init(n);
	for(int i = 1; i <= q; ++i){
		int now = p[i], R = d[now].c2;
		if(d[now].c2 > d[now].c1){
			for(int j = d[now].r1 + 1; j <= d[now].r2; j = s[R].fa(j) + 1)Ckmi(mil[j][d[now].c2], d[now].c1);
			for(int j = d[now].r1 + 1; j < d[now].r2; j = s[R].fa(j))s[R].merge(j, j + 1);
		}
	}
	for(int i = 1; i <= n; ++i)
		for(int j = m - 1; j >= 1; --j)Ckmi(mil[i][j], mil[i][j + 1]);
	for(int i = 1; i <= m; ++i)
		for(int j = n - 1; j >= 1; --j)Ckmi(miu[j][i], miu[j + 1][i]);
	int ans1 = 0, ans2 = 0;
	for(int i = 1; i <= m; ++i)qu[i].init(i - 1);
	for(int i = 1; i <= n; ++i){
		qr[i].init(i - 1);
		for(int j = 1; j <= m; ++j){
			int l = mil[i][j];
			if(i > 1){
				int qf = qr[i].qf(l);
				if(qf + 1 > f[i][j]){
					f[i][j] = qf + 1;
					g[i][j] = qr[i].qg();
				}else if(qf + 1 == f[i][j])add(g[i][j], qr[i].qg());
				qr[i].push(j);
			}
			int u = miu[i][j];
			if(j > 1){
				int qf = qu[j].qf(u);
				if(qf + 1 > f[i][j]){
					f[i][j] = qf + 1;
					g[i][j] = qu[j].qg();
				}else if(qf + 1 == f[i][j])add(g[i][j], qu[j].qg());
				qu[j].push(i);
			}
			if(l < j && u < i && f[i - 1][j - 1] + 1 == f[i][j])add(g[i][j], mod - g[i - 1][j - 1]);
			if(f[i][j] > ans1)ans1 = f[i][j], ans2 = g[i][j];
			else if(f[i][j] == ans1)add(ans2, g[i][j]);
		}
		qr[i].clear();
	}
	for(int i = 1; i <= m; ++i)qu[i].clear();
	printf("%d %d\n",ans1, ans2);
}

int main(){
	freopen("path.in","r",stdin);
	freopen("path.out","w", stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

D. 生成树的传说

先考虑判定问题,可以贪心处理,枚举当前值 , 把 \(l\) 小于等于当前值的 \(r\) 加入堆,每次匹配 \(r\) 最小的

考虑处理最小生成树的限制,我们实际上是有很多形如 \(w_i > w_j\) 的限制

那么令 \(l_i = max(l_i, l_j + 1) r_j = min(r_j, r_i - 1)\)这样 \(j\)\(i\)先入堆先出堆,一定符合题意

现在考虑构造字典序最小的方案,枚举答案 \(k\)

根据 \(hall\) 定理, \(k\) 合法当且仅当不存在 \(L <= k <= R ,R - L + 1 <= \sum[L <= l_i <= r_i <= R]\)

对式子移项, \(R <= L - 1 + \sum[]\) 于是枚举 \(R\), 线段树维护右边的,查询最大值,然后线段树二分到最左侧不合法的 \(L\),把这一段标记成不能选,最后选能选的最小数即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 3505;
void ckmi(int &x, int y){x = x < y ? x : y;}
void ckmx(int &x, int y){x = x > y ? x : y;}
int n, m;
struct node{int u, v, l, r;}d[maxn];
int head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void add(int u, int v){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
}
int fa[maxn], dep[maxn], id[maxn];
void pre(int x){
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x])continue;
		fa[v] = x; dep[v] = dep[x] + 1; id[v] = (i + 1) >> 1;
		pre(v);
	}
}
void upd(int x){
	int u = d[x].u, v = d[x].v;
	while(u != v){
		if(dep[u] < dep[v])swap(u, v);
		ckmi(d[id[u]].r, d[x].r - 1);
		ckmx(d[x].l, d[id[u]].l + 1);
		u = fa[u]; 
	}
}
int pl[maxn];
priority_queue<int, vector<int>, greater<int>>q;
bool cmp(int x, int y){return d[x].l < d[y].l;}
bool check(){
	for(int i = 1; i <= m; ++i)pl[i] = i;
	sort(pl + 1, pl + m + 1, cmp);
	int p = 1;
	for(int i = 1; i <= m; ++i){
		while(p <= m && d[pl[p]].l <= i)q.push(d[pl[p]].r), ++p;
		if(q.empty())return false;
		if(q.top() < i)return false;
		q.pop();
	}
	return true;
}
struct seg{
	struct node{
		int val, tag;
	}t[maxn << 2 | 1];
	void upd(int x, int val){t[x].val += val; t[x].tag += val;}
	void push_up(int x){t[x].val = max(t[x << 1].val, t[x << 1 | 1].val);}
	void push_down(int x){upd(x << 1, t[x].tag); upd(x << 1 | 1, t[x].tag); t[x].tag = 0;}
	void modify(int x, int l, int r, int L, int R, int val){
		if(L <= l && r <= R)return upd(x, val);
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
	void built(int x, int l, int r){
		t[x].tag = 0;
		if(l == r){
			t[x].val = l - 1;
			return;
		}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	int query(int x, int l, int r, int val){
		if(l == r){
			if(t[x].val >= val)return l;
			else return -1;
		}
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1;
		if(t[x << 1].val >= val)return query(x << 1, l, mid, val);
		else return query(x << 1 | 1, mid + 1, r, val);
	}
}t;
int val[maxn], cf[maxn];
bool vis[maxn];
vector<int>v[maxn];
void sol(){
	for(int i = m; i >= 1; --i)v[d[i].r].push_back(d[i].l);
	for(int i = 1; i <= m; ++i){
		t.built(1, 1, m);
		v[d[i].r].pop_back();
		for(int j = 1; j <= m; ++j)cf[j] = 0;
		for(int r = 1; r <= m; ++r){
			if(vis[r])t.modify(1, 1, m, 1, r, 1);
			for(int x : v[r])t.modify(1, 1, m, 1, x, 1);
			if(r < d[i].l)continue;
			int pos = t.query(1, 1, m, r);
			if(pos != -1)++cf[pos], --cf[r + 1];
		}
		for(int j = 1; j <= m; ++j)cf[j] += cf[j - 1];
		for(int j = d[i].l; j <= d[i].r; ++j)if(cf[j] == 0 && !vis[j]){
			vis[j] = true; val[i] = j; break;
		}
	}
	for(int i = 1; i <= m; ++i)printf("%d ",val[i]);printf("\n");
}
int main(){
	freopen("mst.in","r",stdin);
	freopen("mst.out","w",stdout);
	n = read(), m = read();
	bool flag = true;
	for(int i = 1; i <= m; ++i){
		d[i].u = read(), d[i].v = read();
		d[i].l = read(), d[i].r = read();
		if(i < n)add(d[i].u, d[i].v), add(d[i].v, d[i].u);
	}
	pre(1);
	for(int i = n; i <= m; ++i)upd(i);
	if(check())sol();
	else printf("-1\n");
	return 0;
}

posted @ 2022-11-13 18:58  Chen_jr  阅读(27)  评论(4编辑  收藏  举报