2023冲刺国赛自测5

怎么都能切题啊、、

怎么就我是个暴力老哥啊。。。。

这样下去国赛岂不是打铁了。。。。。。。。。

A. 今晚九点

从一个点向其一步走到的位置连边,权值为 1, 向相邻的点连边,权值为 2

跑最短路就是答案。

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

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 = 1005;
int n, m;
int id(int i, int j){return (i - 1) * m + j;}
char s[maxn][maxn];
int head[maxn * maxn], tot;
struct edge{int to, net, val;}e[maxn * maxn * 8];
void add(int u, int v, int w){
	e[++tot] = {v, head[u], w};
	head[u] = tot;
}
int pre[maxn];
int dis[maxn * maxn];
bool vis[maxn * maxn];
priority_queue<pii, vector<pii>, greater<pii>>q;
int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n = read(), m = read();
	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] == '#')pre[j] = 0;
			else{
				if(i == 1 || s[i - 1][j] == '#')pre[j] = i;
				else if(pre[j])add(id(i, j), id(pre[j], j), 1);
			}
	}
	for(int i = 1; i <= m; ++i)pre[i] = 0;
	for(int i = n; i >= 1; --i){
		for(int j = 1; j <= m; ++j)
			if(s[i][j] == '#')pre[j] = 0;
			else{
				if(i == n || s[i + 1][j] == '#')pre[j] = i;
				else if(pre[j])add(id(i, j), id(pre[j], j), 1);
			}
	}
	for(int i = 1; i <= n; ++i){
		int pre = 0;
		for(int j = 1; j <= m; ++j)
			if(s[i][j] == '#')pre = 0;
			else{
				if(j == 1 || s[i][j - 1] == '#')pre = j;
				else if(pre)add(id(i, j), id(i, pre), 1);
			}
		pre = 0;
		for(int j = m; j >= 1; --j)
			if(s[i][j] == '#')pre = 0;
			else{
				if(j == m || s[i][j + 1] == '#')pre = j;
				else if(pre)add(id(i, j), id(i, pre), 1);
			}
	}
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if(s[i][j] == '.'){
				if(s[i - 1][j] == '.')add(id(i, j), id(i - 1, j), 2);
				if(s[i + 1][j] == '.')add(id(i, j), id(i + 1, j), 2);
				if(s[i][j + 1] == '.')add(id(i, j), id(i, j + 1), 2);
				if(s[i][j - 1] == '.')add(id(i, j), id(i, j - 1), 2);
			}
	int a = read(), b = read(), c = read(), d = read();
	int des = id(c, d);
	memset(dis, 0x3f, sizeof(dis)); 
	dis[id(a, b)] = 0; q.push(pii(0, id(a, b)));
	while(!q.empty()){
		int x = q.top().second; q.pop();
		if(vis[x])continue;
		vis[x] = true;
		if(x == des)break;
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			if(dis[v] > dis[x] + e[i].val){
				dis[v] = dis[x] + e[i].val;
				q.push(pii(dis[v], v));
			}
		}
	}	
	printf("%d\n",dis[des] == 0x3f3f3f3f ? -1 : dis[des]);
	return 0;
}

B. 小王唱歌

我咋这么菜,但凡是个数据结构就不会写+调半年。

哦,我是啥都不会,啥都得调半年啊,nmsl。

fx,i 表示 x 选择颜色 i 其子树内方案数

转移为

fx,i=vson(x)(j!=ifv,j)

值域显然可以离散化。

考虑如何优化这个过程。

f 的值为连续段,那么可以用珂朵莉树的思想进行维护

加上启发式合并,发现复杂度是 nlog2 的,可以接受

实现起来细节挺多的吧?但是 Delov 说直接无脑写就行。

还是我太菜了。

你需要实现区间加法乘法求和,用来快速把 fx,i 变成 j!=ifx,j

然后因为 b 不同,你需要把儿子强制对齐成父亲的长度

其实都是常规?操作

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

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 mod = 998244353, maxn = 5e5 + 55;
mt19937 rd(0);
int sint(){return rd();}
int n, m, a[maxn], b[maxn];
vector<int>g[maxn];
struct FHQ{
	struct node{
		int sum, multag, addtag, len, si, l, r, val, f, key, slen;
	}t[maxn * 2];
	int cnt = 0;
	int bin[maxn * 2], tp;
	int Nid(){if(tp)return bin[tp--]; return ++cnt;}
	int New(int val, int len, int f){
		int ct = Nid();
		t[ct] = {(int)(1ll * len * f % mod), 1, 0, len, 1, 0, 0, val, f, sint(), len};
		return ct;
	}
	void push_up(int x){
		t[x].sum = (t[t[x].l].sum + t[t[x].r].sum + 1ll * t[x].f * t[x].len) % mod;
		t[x].si = t[t[x].l].si + t[t[x].r].si + 1;
		t[x].slen = t[t[x].l].slen + t[t[x].r].slen + t[x].len;
	}
	void mul(int x, int v){
		t[x].addtag = 1ll * t[x].addtag * v % mod; 
		t[x].f = 1ll * t[x].f * v % mod; 
		t[x].multag = 1ll * t[x].multag * v % mod;
		t[x].sum = 1ll * t[x].sum * v % mod;
	}
	void add(int x, int v){
		t[x].addtag = (t[x].addtag + v) % mod; 
		t[x].f = (t[x].f + v) % mod; 
		t[x].sum = (t[x].sum + 1ll * t[x].slen * v) % mod;
	}
	void push_down(int x){
		if(t[x].multag != 1){
			if(t[x].l)mul(t[x].l, t[x].multag);
			if(t[x].r)mul(t[x].r, t[x].multag);
			t[x].multag = 1;
		}
		if(t[x].addtag){
			if(t[x].l)add(t[x].l, t[x].addtag);
			if(t[x].r)add(t[x].r, t[x].addtag);
			t[x].addtag = 0;
		}
	}
	void split(int x, int k, int &l, int &r){
		if(!x){l = r = 0;return;} 
		push_down(x);
		if(t[x].val <= k){l = x; split(t[x].r, k, t[x].r, r); push_up(x);}
		else{r = x; split(t[x].l, k, l, t[x].l); push_up(x);}
	}
	int merge(int x, int y){
		if(!x || !y)return x | y;
		if(t[x].key < t[y].key){
			push_down(x); t[x].r = merge(t[x].r, y); push_up(x); return x;
		}else{
			push_down(y); t[y].l = merge(x, t[y].l); push_up(y); return y;
		}
	}
	void get(int &rt, int x){rt = New(1, a[b[x]], 1);}
	int st[maxn], top;
	void dfs(int x){
		push_down(x);
		if(t[x].l)dfs(t[x].l);
		st[++top] = x;
		if(t[x].r)dfs(t[x].r);
	}
	vector<int>stk;
	void mul(int &rt, int L, int R, int val){
		int l = 0, r = 0, m = 0;
		split(rt, L - 1, l, r);
		split(r, R, m, r);
		int x = m; while(t[x].r)stk.push_back(x), push_down(x), x = t[x].r; stk.push_back(x);
		if(t[x].val + t[x].len - 1 > R){
			r = merge(New(R + 1, t[x].len - (R - t[x].val + 1), t[x].f), r);
			t[x].len = R - t[x].val + 1; 
		}
		while(stk.size())push_up(stk.back()), stk.pop_back();
		mul(m, val);
		rt = merge(merge(l, m), r);
	}
	void change(int &rt, int R){
		int sum = t[rt].sum;  
		int r = 0;
		split(rt, R, rt, r);
		int x = rt; while(t[x].r)stk.push_back(x), push_down(x), x = t[x].r; stk.push_back(x);
		if(t[x].val + t[x].len - 1 < R)t[x].r = New(t[x].val + t[x].len, R - t[x].val - t[x].len + 1, 0);
		else t[x].len = R - t[x].val + 1; 
		while(stk.size())push_up(stk.back()), stk.pop_back();
		mul(rt, mod - 1); add(rt, sum);
		top = 0; if(r)dfs(r); for(int i = 1; i <= top; ++i)bin[++tp] = st[i];

	}
	void merge_mul(int &rt1, int &rt2){
		if(t[rt1].si < t[rt2].si)swap(rt1, rt2);
		top = 0; dfs(rt2); 
		for(int i = 1; i <= top; ++i){
			int l = t[st[i]].val, r = t[st[i]].val + t[st[i]].len - 1;
			mul(rt1, l, r, t[st[i]].f);
		}
		for(int i = 1; i <= top; ++i)bin[++tp] = st[i];
	}
}T;
int rt[maxn];
void solve(int x, int fa){
	T.get(rt[x], x);
	for(int v : g[x])if(v != fa){
		solve(v, x);
		T.change(rt[v], a[b[x]]);
		T.merge_mul(rt[x], rt[v]);
	}
}
int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)b[i] = a[i] = read();
	sort(a + 1, a + n + 1); m = unique(a + 1, a + n + 1) - a - 1;
	for(int i = 1; i <= n; ++i)b[i] = lower_bound(a + 1, a + m + 1, b[i]) - a;
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		g[u].push_back(v); g[v].push_back(u);
	}
	solve(1, 0);
	printf("%d\n", T.t[rt[1]].sum);
	return 0;
}

C. 不见不散

经典结论:交换序列上任意两数,逆序对数变化为奇数

那么逆序对数与 n(n1)2 奇偶性不同的一定无解

否则一定有解

考虑某个人需要的东西如果在 x 手中,那么可以让他与每个人交换一次,最后与 x 交换,这样可以删去他得到一个子问题。

然后问题等价于特殊性质 ai=i

由于上面的结论,有解的话点数一定是 4n/4n+1

那么可以 4 个点一起删去

删去的方法可以构造出来,详细见程序

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

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 = 1005;
int n, a[maxn];
vector<pii>ans;
vector<int>s;
void swa(int x, int y){swap(a[x], a[y]); ans.push_back(pii(x, y));}
void solve(int x,int y){
	for(int i : s)swa(x, i);
	swa(x, y); reverse(s.begin(),s.end());
	for(int i : s)swa(y, i);
}
void del4(){
	int x = s.back(); s.pop_back();
	int y = s.back(); s.pop_back();
	int z = s.back(); s.pop_back();
	int w = s.back(); s.pop_back();
	solve(x, y); solve(z, w);
	swa(x, z); swa(y, w); swa(x, w); swa(y, z);
}
void print(){
	printf("YES\n");
	for(pii v : ans)printf("%d %d\n", v.first, v.second);
}
int main(){
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	n = read();	for(int i = 1; i <= n; ++i)a[read()] = i;
	for(int i = 1; i <= n; ++i)s.push_back(i);
	for(int round = 1; round <= n; ++round){
		int x = 0; for(int i : s)if(a[i] != i){x = i; break;}
		if(!x)break;
		int y; for(int i : s)if(a[i] == x){y = i; break;}
		for(int i : s)if(i != x && i != y)swa(x, i);
		swa(x, y);
		for(int i = 0; i < s.size() - 1; ++i)if(s[i] == x)swap(s[i], s[i + 1]); s.pop_back();
	}
	while(s.size() > 3)del4();
	if(s.size() > 1)printf("NO\n"); else print();
	return 0;
}
posted @   Chen_jr  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示