zoukankan      html  css  js  c++  java
  • 20210925模拟赛

    2021.09.25 模拟赛

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

    赛时

    (7:30) 开题。

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

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

    (7:50) 开始做题。

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

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

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

    此时是 (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) 所在的集合都要 ( imesdfrac 23)(v) 所在的集合都要 ( imesdfrac13)

    (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
    ", 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 imes2 eq n imes m) , 则经过旋转,一定能达到 (ans) 。(因为其中 (n imes m-ans imes2) 的点可以作为转移站来转移)
    • 如果 (ans imes 2 = n imes m) , 那么需要考虑:
      • 假设左右向的鞋子分别为 (0,2) ,则他们之和为 (2)(0) (pmod 4)
      • 这样,每有一对左右向的鞋子,就把 (tot += 2) 。 最后考虑:如果$tot eq sum pmod 4 $ , 就必须强造一个中转站来转移,即 (ansleftarrow 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)
    ",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)
    ",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
    */
    
  • 相关阅读:
    Python深入05 装饰器
    Python深入04 闭包
    Python深入03 对象的属性
    Ubuntu (虚拟机同样) 更换内核?
    .out
    GCC 编译详解
    linux 编译内核 /boot空间不足?
    Java Swing提供的文件选择对话框
    Java Swing 实时刷新JTextArea,以显示不断append的内容?
    为什么要编译Linux内核?
  • 原文地址:https://www.cnblogs.com/Shinomiya/p/15336505.html
Copyright © 2011-2022 走看看