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\)
没有写出……
这个做法是确实没有想出。
应该枚举从 \(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
*/