20210925模拟赛

2021.09.25 模拟赛

想拿到的分都拿到了,拿不到的分也确实没有拿到……

赛时

\(7:30\) 开题。

看到 \(T_1\) 是做过的原题,稍微兴奋了一下。

感觉 \(T_2\) 非常不可做, \(T_3\) 大概是个 \(DP\)\(T_4\) 可能是个阴间构造题。

\(7:50\) 开始做题。

大概用了 \(20\min\) 思考+写完 \(T_1\) ,自己写了几组数据都可过。随后想起大样例,也能过。于是放心。

剩下的时间,用了两个多小时\(T_3\) , 但是一直没有结果……

然后看了一下 \(T_2\) ,想到其 \(60pts\) 实现不是很难。推了约 \(20\min\) 后发现式子。用并查集维护这个过程。

此时是 \(11:10\)。原本准备用剩下的时间写完 \(T_3\)\(15\) 分暴力。

但是此时教练宣布 比赛 \(11:30\) 结束。

一下慌了神。于是手抖着口胡了 \(T_2\) 的满分做法,但是没有时间做 \(T_3\)\(T_4\) 了。

赛后

\(100+60+0+0=160\)

\(T_1\) 是做过的原题,没有出问题。

放一下之前写的博客:【YBTOJ】【单调队列优化DP】写博客

也幸亏当时做题时比较认真,想了很多相关的事情,于是能够不丢分。

\(T_2\)\(60\) 分做法没有问题,但是最后紧急想的做法错误。

此题正解:

可以知道,每次合并, \(u\) 所在的集合都要 \(\times\dfrac 23\)\(v\) 所在的集合都要 \(\times\dfrac13\)

\(u\)\(v\) 之间转移,相当于建边并建出了一棵树。

通过处理树的 DFS 序,可以使所有操作变为连续。

注意: DFS 序应严格按照加边顺序的先后来进行。

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f , N = 2e5+5 , mod = 998244353;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret=0;char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9')) ch = c  , c = getchar();
	while(c >= '0' && c <= '9') ret = (ret << 1) + (ret << 3) + c - '0', c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,m;
int f[N],siz[N];
int find(int x){ return f[x] == x ? x : f[x] = find(f[x]);}
inline void merge(int x,int y){ siz[y] += siz[x], f[find(x)] = find(y);}
inline ll qpow(ll a,int b){
	ll ret = 1;
	while(b){
		if(b & 1) (ret *= a) %= mod;
		(a *= a) %= mod; b >>= 1;
	}
	return ret;
}
ll ifac;
struct Segtre{ll laz,sum;}tre[N<<2];
void build(int k,int l,int r){
	tre[k].laz = 1;
	if(l == r){tre[k].sum = 3;return;}
	int mid = (l + r) >> 1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	tre[k].sum = (tre[k<<1].sum + tre[k<<1|1].sum) % mod;
}
inline void modi(int k,ll w){
	(tre[k].sum *= w) %= mod;
	(tre[k].laz *= w) %= mod;
}
inline void pushdown(int k){
	if(tre[k].laz == 1) return;
	modi(k<<1,tre[k].laz);
	modi(k<<1|1,tre[k].laz);
	tre[k].laz = 1;
}
void modify(int k,int l,int r,int x,int y,ll w){
	if(x <= l && r <= y) {modi(k,w);return;}
	pushdown(k);
	int mid = (l + r) >> 1;
	if(x <= mid) modify(k<<1,l,mid,x,y,w);
	if(y > mid)  modify(k<<1|1,mid+1,r,x,y,w);
	tre[k].sum = (tre[k<<1].sum + tre[k<<1|1].sum) % mod;
}
ll query(int k,int l,int r,int x){
	if(l == r) return tre[k].sum;
	pushdown(k);
	int mid = (l + r) >> 1;
	if(x <= mid) return query(k<<1,l,mid,x);
	else return query(k<<1|1,mid+1,r,x);
}
ll querysum(int k,int l,int r,int x,int y){
	if(x <= l && r <= y) return tre[k].sum;
	pushdown(k);
	int mid = (l + r) >> 1; ll ret = 0;
	if(x <= mid) ret += querysum(k<<1,l,mid,x,y);
	if(y > mid)  ret += querysum(k<<1|1,mid+1,r,x,y);
	return ret % mod;
}
struct ope{int op,u,v;}o[N];
struct Edge{int to,nxt;}e[N<<1];
int head[N],ecnt = -1;
inline void add_edge(int u,int v){e[++ecnt] = (Edge){v,head[u]}; head[u] = ecnt;}
int dfn[N],Dfn = -1,tsiz[N];
void dfs(int u,int _f){
	dfn[u] = ++Dfn, tsiz[u] = 1;
	vector<int> to;
	for(int i = head[u] ; ~i ; i = e[i].nxt)
	to.push_back(e[i].to);
	for(int i = to.size()-1 ; i >= 0 ; i --){
		int v = to[i];
		if(v == _f) continue;
		dfs(v,u);
		tsiz[u] += tsiz[v];
	}
}
bool vis[N];
signed main(){
//	fo("zoo");
	n = read(), m = read();
	ifac = qpow(3,mod-2);
	for(int i = 1 ; i <= n ; i ++) f[i] = i, siz[i] = 1;

	memset(head,-1,sizeof(head));
	for(int i = 1 ; i <= m ; i ++){
		o[i].op = read();
		if(o[i].op == 1) {
			o[i].u = read(), o[i].v = read();
			add_edge(o[i].u,o[i].v),
			vis[o[i].v] = 1;
		}
		else o[i].u = read();
	}
	for(int i = 1 ; i <= n ; i ++) if(!vis[i]) add_edge(0,i);
	dfs(0,0);
	build(1,1,n);
	for(int i = 1 ; i <= m ; i ++){
		switch(o[i].op){
			case 1:{
				int u = o[i].u, v = o[i].v;
				ll sumv = querysum(1,1,n,dfn[v],dfn[v]+tsiz[v]-1),
				sumu = querysum(1,1,n,dfn[u],dfn[v]-1);
				modify(1,1,n,dfn[u],dfn[v]-1,2*ifac*sumv%mod);
				modify(1,1,n,dfn[v],dfn[v]+tsiz[v]-1,ifac*(sumu) % mod);
				merge(v,u);
				break;
			}
			default:{
				int u = o[i].u;
				printf("%lld\n", query(1,1,n,dfn[u]) * qpow(3,n-siz[find(u)]) % mod);
				break;
			}
		}
	}
}

\(T_3\)

没有写出……

image

这个做法是确实没有想出。

应该枚举从 \(a_x\) 改为 \(a_y\) 来尝试转移。

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 5e5+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret=0;char ch=' ',c=getchar();
	while(!(c>='0'&&c<='9'))ch=c,c=getchar();
	while(c>='0'&&c<='9')ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
int a[N],b[N],nxt[N],n,m;
ll sum[N],ans,del;
bool vis[N];
set <int> s;
map <int,int> lst;

template <typename T> struct Treary{
	T c[N];
	Treary(){memset(c,0xc0,sizeof(c));}
	inline int lowbit(int x){return x & -x;}
	inline void add(int x,T v){
		for(int i = x ; i <= n ; i += lowbit(i))
			c[i] = max(c[i],v);
	}
	inline ll query(int x){
		ll ret = -INF;
		for(int i = x ; i ; i -= lowbit(i))
			ret = max(ret,c[i]);
		return ret;
	}
};
Treary<ll> tr1,tr2;
int main(){
	n = read();
	for(int i = n ; i ; i --) sum[i] = sum[i + 1] + i;
	for(int i = 1 ; i <= n ; i ++){
		a[i] = read();
		if (!lst[a[i]]) 
			ans += sum[i], vis[i] = 1, b[++m] = a[i];
		else 
			nxt[lst[a[i]]] = i;
		lst[a[i]] = i;
		nxt[i] = n + 1;
	}
	sort(b+1,b+m+1);
	
	s.insert(-INF), s.insert(INF);
	for(int i = 1 ; i <= n ; i ++)
		if (vis[i]){
			set<int>::iterator it = s.lower_bound(a[i]);
			del = min(del,-sum[i]+sum[nxt[i]]+ *it - a[i]);
			del = min(del,-sum[i]+sum[nxt[i]]- *(--it) + a[i]);
			s.insert(a[i]);
		}
	
	for(int i = n ; i ; i --)
		if (vis[i]){
			int pos = lower_bound(b + 1, b + m + 1, a[i]) - b;
			del = min(del,sum[nxt[i]]+a[i]-tr1.query(pos));
			del = min(del,sum[nxt[i]]-a[i]-tr2.query(n-pos+1));
			tr1.add(pos,sum[i]+a[i]);
			tr2.add(n-pos+1,sum[i]-a[i]);
		}
	printf("%lld",ans + del);
	return 0;
}

\(T_4\)

发现所有的旋转操作,总权值在 \(\mod4\) 意义下是不变的。

通过这个性质,可以先将相邻的左右脚鞋子建边。

跑出来一个二分图最大匹配,设答案是 \(ans\)

  • 如果 \(ans\times2 \neq n\times m\) , 则经过旋转,一定能达到 \(ans\) 。(因为其中 \(n\times m-ans\times2\) 的点可以作为转移站来转移)
  • 如果 \(ans \times 2 = n\times m\) , 那么需要考虑:
    • 假设左右向的鞋子分别为 \(0,2\) ,则他们之和为 \(2\)\(0\) \(\pmod 4\)
    • 这样,每有一对左右向的鞋子,就把 \(tot += 2\) 。 最后考虑:如果$tot \neq sum \pmod 4 $ , 就必须强造一个中转站来转移,即 \(ans\leftarrow ans-1\)
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 1e2+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret=0;char ch=' ',c=getchar();
	while(!(c>='0'&&c<='9'))ch=c,c=getchar();
	while(c>='0'&&c<='9')ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
int n,m;
const int dir[4][2] = {{0,-1},{0,1},{-1,0},{1,0}};
int sum;
inline int getpos(int i,int j){return (i-1)*m+j;}
vector<int> v1,v2;
struct Edge{int to,nxt;}e[N*N*4];
int head[N*N],ecnt = -1;
inline void add_edge(int u,int v){/*printf(" add(%d,%d)\n",u,v);*/e[++ecnt] = (Edge){v,head[u]}; head[u] = ecnt;}
int bel[N*N], vis[N*N];
bool dfs(int u,int t){
//	printf(" DFS(%d,%d)\n",u,t);
	for(int i = head[u] ; ~i ; i = e[i].nxt){
		int v = e[i].to;
		if(vis[v] != t){
			vis[v] = t;
			if(!bel[v] || dfs(bel[v],t)) {
				bel[v] = u;
				return 1;
			}
		}
	}
	return 0;
}
char a[N][N];
bool vis1[N*N];
signed main(){
	memset(head,-1,sizeof(head));
	n = read(), m = read();
	for(int i = 1 ; i <= n ; i ++)
		scanf("%s",a[i]+1);
	for(int i = 1 ; i <= n ; i ++){
		char ch[N]; scanf("%s",ch+1);
		for(int j = 1 ; j <= m ; j ++)
			(sum += ch[j] == 'L' ? 0 : ch[j] == 'U' ? 1 : ch[j] == 'R' ? 2 : 3) %= 4;
	}
	for(int i = 1 ; i <= n ; i ++)
		for(int j = 1 ; j <= m ; j ++)
			if((i+j) & 1)
				for(int d = 0 ; d < 4 ; d ++){
					int ti = i + dir[d][0], tj = j + dir[d][1];
					if(ti<1 || ti>n || tj<1 || tj>m) continue;
					if(a[i][j] != a[ti][tj])
						add_edge(getpos(i,j),getpos(ti,tj)),
						v1.push_back(getpos(i,j)),
						v2.push_back(getpos(ti,tj));
				}
	int ans = 0;
	for(int i = 0 ; i <= (int)v1.size() ; i ++)
		if(!vis1[v1[i]]){
			vis1[v1[i]] = 1;
			if(dfs(v1[i],v1[i]))
				ans ++;
		}
			
	if(2*ans != n*m) printf("%d",ans);
	else{
		int tot = 0;
		for(int i = 1 ; i <= n ; i ++)
			for(int j = 1 ; j <= m ; j ++)
				if(!((i+j) & 1))
					if((bel[getpos(i,j)]-1) / m + 1 == i) 
						(tot += 2) %= 4;
		if(tot != sum) ans --;
		printf("%d",ans);
	}
}
/*
2 2
RL
LR
UR
LU
*/
posted @ 2021-09-26 09:10  Last-Order  阅读(59)  评论(1编辑  收藏  举报