zoukankan      html  css  js  c++  java
  • 20联赛集训day5 题解

    A

    枚举每个矩形,前缀和优化即可。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 2000+5;
    
    char str[MAXN];
    int n,k;
    int sm[MAXN][MAXN];
    
    int main(){
    	scanf("%d%d",&n,&k);
    	FOR(i,1,n){
    		scanf("%s",str+1);
    		FOR(j,1,n){
    			sm[i][j] = sm[i][j-1]+sm[i-1][j]-sm[i-1][j-1]+str[j]-'0';
    		}
    	}
    	int ans = 0;
    	FOR(i,k,n){
    		FOR(j,k,n){
    			ans = std::max(ans,sm[i][j]-sm[i-k][j]-sm[i][j-k]+sm[i-k][j-k]);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    B

    (O(n2^n)) 可以做任意图,只需要考虑如何判断一个图是强连通图:有一个点在正图和反图上都能到达所有点。

    这个题要注意到竞赛图缩点后 DAG 上拓扑序小的点向拓扑序大的点都有连边。

    所以我们如果得到了一个集合 (S) 是强连通的,设属于它的所有的点的出边的交为 (T),对于任意 (R subset T,R eq emptyset),一定 (S | R) 是不合法的区间。而我们发现这样顶多会不重不漏遍历每个状态一次,所以均摊复杂度是 (O(2^n)) 的。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 24+1;
    int n;
    int e[MAXN],out[(1<<MAXN)+3],pc[(1<<MAXN)+3];
    #define lowbit(x) ((x)&(-(x)))
    bool f[(1<<MAXN)+3];
    
    inline void Solve(){
    	scanf("%d",&n);CLR(e,0);CLR(f,0);
    	FOR(i,0,n-1){
    		FOR(j,0,n-1){
    			int x;scanf("%d",&x);
    			e[i] |= (x<<j);
    		}
    	}
    	out[0] = (1<<n)-1;
    	FOR(S,1,(1<<n)-1) out[S] = out[S-lowbit(S)]&e[pc[lowbit(S)]];
    	int ans = 1;
    	FOR(S,1,(1<<n)-1){
    		if(!f[S]){
    			ans++;
    			for(int T = out[S];T;T = (T-1)&out[S]){
    				f[S|T] = 1;
    			}
    		}
    	}
    	printf("%d
    ",ans);
    }
    
    int main(){
    	FOR(i,0,23) pc[1<<i] = i;
    	int T;scanf("%d",&T);
    	while(T--) Solve();
    	return 0;
    }
    

    C

    我们先来观察一下什么情况是不合法的。我们按轮考虑:

    设数字 (i) 在三个人的序列出现的位置分别是 (pa_i,pb_i,pc_i);设在第 (i) 轮三个人选的数字是 (x,y,z),合法当且仅当

    • (x,y,z) 互不相同
    • (x)(B,C) 的喜好序列出现的位置在 (y,z) 后面,其余同理

    所以我们发现我们如果确定了这三个人选什么数的话, C 的排列的方案数都是不变的,可以设 (g_{i,j}) 表示考虑确定了 C 的前 (i) 位,已经进行了 (j) 轮的方案数,转移可以枚举这一位是否是 C 最终选的数字中的一个位置,如果是就 (g_{i,j} = g_{i-1,j-1}(2j-(i-j))),否则就直接 (g_{i,j} = g_{i-1,j})

    然后我们现在就是要对有多少种不同的选数方案进行dp;设 (f_{i,j,k}) 表示 A 选了它自己的序列中第 (i) 个,B 选了第 (j) 个,C 还空着 (k) 个的方案数。枚举下一次选的位置 (i',j'),我们发现首先 (i') 在 B 中出现的位置不能在 (j') 之前,(j') 在 A 中出现的位置不能在 (i') 之前。如果在 ([i,i'],[j,j']) 有个数字没有被选过,一定是被 (C) 选了,我们设这样的数字数量为 (x)。转移就是 (f_{i,j,k} o k^{underline x}f_{i',j',k+1-x})

    这样复杂度就是 (O(n^6)) 的。

    首先我们可以预处理找 (x) 的过程,复杂度变成了 (O(n^5))

    我们考虑每次可以不用一起枚举 (i',j') ,可以设 (f1,f2) 表示两个人的 dp 数组,所以复杂度变成了 (O(n^4))

    我们发现每次只需要考虑 (i+1) 的位置是否选择,选择了就换到另一个人的 dp 数组转移,复杂度 (O(n^3))。注意到我们从第一个人转移到第二个人不用考虑任何限制,在第二个人转移回去的时候统一考虑即可。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 400+5;
    const int ha = 1e9 + 7;
    int n,a[MAXN],b[MAXN],pa[MAXN],pb[MAXN]; 
    int f1[MAXN][MAXN][MAXN],f2[MAXN][MAXN][MAXN];
    // A选了第i个,B选了第j个,C还能用k个 
    int g[MAXN][MAXN];
    // 前i个 选了j个 
    
    inline void add(int &x,int y){
    	x += y;if(x >= ha) x -= ha;
    }
    
    int main(){
    	scanf("%d",&n);
    	FOR(i,1,n) scanf("%d",a+i),pa[a[i]] = i;
    	FOR(i,1,n) scanf("%d",b+i),pb[b[i]] = i;
    	f1[1][1][1] = 1;
    	FOR(i,1,n+1){
    		FOR(j,1,n+1){
    			FOR(k,0,n/3){
    				if(f1[i][j][k]){
    					if(k && pb[a[i+1]] > j) add(f1[i+1][j][k-1],1ll*f1[i][j][k]*k%ha);
    					if(pb[a[i+1]] <= j) add(f1[i+1][j][k],f1[i][j][k]);
    					add(f2[i+1][j][k],f1[i][j][k]);
    				}
    				if(f2[i][j][k]){
    					if(k && pa[b[j+1]] > i) add(f2[i][j+1][k-1],1ll*f2[i][j][k]*k%ha);
    					if(pa[b[j+1]] <= i) add(f2[i][j+1][k],f2[i][j][k]);
    					if(i == n+1 || j == n+1 || (pb[a[i]] > j+1 && pa[b[j+1]] > i)) add(f1[i][j+1][k+1],f2[i][j][k]);
    				}
    			}
    		}
    	}
    	g[0][0] = 1;
    	FOR(i,0,n-1){
    		FOR(j,0,n/3){
    			add(g[i+1][j+1],g[i][j]);
    			if(3*j-i > 0) add(g[i+1][j],1ll*g[i][j]*(3*j-i)%ha);
    		}
    	}
    	int ans = 1ll*f1[n+1][n+1][1]*g[n][n/3]%ha; 
    	printf("%d
    ",ans);
    	return 0;
    }
    

    D

    树上路径问题如果不会了可以考虑链分治,分开维护重链和轻边。

    对于重链的颜色直接用线段树维护区间赋值即可。

    轻边的颜色有两种可能改变:

    1. 有一次修改经过了这个轻边
    2. 有一次修改经过了轻边的两个端点之一,并没有经过轻边

    我们只需要知道这两个操作最后一次的时间,就能判断出轻边的颜色,线段树维护即可。

    要注意是轻边的两个端点都要查,因为可能有某个修改的 lca 到父亲的边是轻边的情况。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 3e5 + 5;
    
    int n,q;
    
    struct Edge{
    	int to,nxt;
    }e[MAXN<<1];
    int head[MAXN],cnt;
    
    inline void add(int u,int v){
    	e[++cnt] = (Edge){v,head[u]};head[u] = cnt;
    	e[++cnt] = (Edge){u,head[v]};head[v] = cnt;
    }
    
    #define lc ((x)<<1)
    #define rc ((x)<<1|1)
    
    int sm[MAXN<<2],ts[MAXN<<2],cov[MAXN<<2],tag[MAXN<<2];
    // 0表示染黑 数字表示有个时间
    
    inline void cover1(int x,int l,int r,int d){
    	sm[x] = (r-l+1)*d;cov[x] = d;
    }
    
    inline void cover2(int x,int l,int r,int d){
    	ts[x] = tag[x] = d;
    }
    
    inline void pushdown(int x,int l,int r){
    	int mid = (l + r) >> 1;
    	if(cov[x] != -1){
    		cover1(lc,l,mid,cov[x]);
    		cover1(rc,mid+1,r,cov[x]);
    		cov[x] = -1;
    	}
    	if(tag[x] != -1){
    		cover2(lc,l,mid,tag[x]);
    		cover2(rc,mid+1,r,tag[x]);
    		tag[x] = -1;
    	}
    }
    
    inline void modify(int x,int l,int r,int L,int R,int d){
    	if(L > R) return;
    	if(l == L && r == R){
    		cover1(x,l,r,d?0:1);
    		if(d) cover2(x,l,r,d);
    		return;
    	}
    	int mid = (l + r) >> 1;pushdown(x,l,r);
    	if(R <= mid) modify(lc,l,mid,L,R,d);
    	else if(L > mid) modify(rc,mid+1,r,L,R,d);
    	else modify(lc,l,mid,L,mid,d),modify(rc,mid+1,r,mid+1,R,d);
    	sm[x] = sm[lc]+sm[rc];
    }
    
    inline void modify2(int x,int l,int r,int L,int R,int d){
    	if(L > R) return;
    	if(l == L && r == R){
    		cover2(x,l,r,d);
    		return;
    	}
    	int mid = (l + r) >> 1;pushdown(x,l,r);
    	if(R <= mid) modify2(lc,l,mid,L,R,d);
    	else if(L > mid) modify2(rc,mid+1,r,L,R,d);
    	else modify2(lc,l,mid,L,mid,d),modify2(rc,mid+1,r,mid+1,R,d);
    	sm[x] = sm[lc]+sm[rc];
    }
    
    inline int query(int x,int l,int r,int L,int R){
    	if(L > R) return 0;
    	if(l == L && r == R) return sm[x];
    	int mid = (l + r) >> 1;pushdown(x,l,r);
    	if(R <= mid) return query(lc,l,mid,L,R);
    	if(L > mid) return query(rc,mid+1,r,L,R);
    	return query(lc,l,mid,L,mid)+query(rc,mid+1,r,mid+1,R);
    }
    
    inline int queryt(int x,int l,int r,int p){
    	if(l == r) return ts[x];
    	int mid = (l + r) >> 1;pushdown(x,l,r);
    	if(p <= mid) return queryt(lc,l,mid,p);
    	else return queryt(rc,mid+1,r,p);
    }
    
    inline void build(int x,int l,int r){
    	sm[x] = r-l+1;tag[x] = cov[x] = -1;
    	if(l == r) return;
    	int mid = (l + r) >> 1;
    	build(lc,l,mid);build(rc,mid+1,r);
    }
    
    int dfn[MAXN],tp[MAXN],son[MAXN],dep[MAXN],fa[MAXN],sz[MAXN],_;
    
    inline void dfs1(int v){
    	dep[v] = dep[fa[v]]+1;sz[v] = 1;
    	for(int i = head[v];i;i = e[i].nxt){
    		if(e[i].to == fa[v]) continue;
    		fa[e[i].to] = v;dfs1(e[i].to);
    		sz[v] += sz[e[i].to];
    		son[v] = sz[son[v]] > sz[e[i].to] ? son[v] : e[i].to;
    	}
    }
    
    inline void dfs2(int v,int tp=1){
    	dfn[v] = ++_;::tp[v] = tp;
    	if(son[v]) dfs2(son[v],tp);
    	for(int i = head[v];i;i = e[i].nxt){
    		if(e[i].to == fa[v] || e[i].to == son[v]) continue;
    		dfs2(e[i].to,e[i].to);
    	}
    }
    
    int tim[MAXN];
    
    inline void modify(int x,int y){
    	if(son[x]) modify(1,1,n,dfn[son[x]],dfn[son[x]],0);
    	if(son[y]) modify(1,1,n,dfn[son[y]],dfn[son[y]],0);
    	while(tp[x] != tp[y]){
    		if(dep[tp[x]] < dep[tp[y]]) std::swap(x,y);
    		if(son[tp[x]]) modify(1,1,n,dfn[son[tp[x]]],dfn[x],_);
    		modify2(1,1,n,dfn[tp[x]],dfn[tp[x]],_);
    		tim[tp[x]] = _;
    		x = fa[tp[x]];
    		if(son[x]) modify(1,1,n,dfn[son[x]],dfn[son[x]],0);
    	}
    	if(dep[x] > dep[y]) std::swap(x,y);
    	modify(1,1,n,dfn[x],dfn[y],_);
    	modify(1,1,n,dfn[x],dfn[x],0);
    }
    
    inline int query(int x,int y){
    	int res = 0;
    	while(tp[x] != tp[y]){
    		if(dep[tp[x]] < dep[tp[y]]) std::swap(x,y);
    		if(son[tp[x]]) res += query(1,1,n,dfn[son[tp[x]]],dfn[x]);
    		if(tim[tp[x]] < std::max(queryt(1,1,n,dfn[fa[tp[x]]]),queryt(1,1,n,dfn[tp[x]]))) res++;
    		x = fa[tp[x]];
    	}
    	if(dep[x] > dep[y]) std::swap(x,y);
    	if(son[x]) res += query(1,1,n,dfn[son[x]],dfn[y]);
    	return res;
    }
    
    int main(){
    	scanf("%d",&n);
    	FOR(i,2,n){
    		int u,v;scanf("%d%d",&u,&v);
    		add(u,v);
    	}
    	scanf("%d",&q);
    	dfs1(1);dfs2(1);build(1,1,n);
    	FOR(i,1,n) tim[i] = -1;
    	FOR(i,1,q){
    		_ = i;int opt,x,y;scanf("%d%d%d",&opt,&x,&y);
    		// if(i == 2) FF = 1;
    		if(opt == 1) modify(x,y);
    		else printf("%d
    ",query(x,y));
    	}
    	return 0;
    }
    
  • 相关阅读:
    shop++之language
    shop++改造之ResponseEntity的坑
    shop++改造之Filter类
    mysql关联模糊查询他表字段
    mysql一张表多个字段关联另一张表查询
    html页面导出为excel表格
    layui打印html页面转成pdf
    jQuery视频格式的验证
    jQuery图片灯箱和视频灯箱
    空间谱专题16:信号个数估计
  • 原文地址:https://www.cnblogs.com/rainair/p/14305502.html
Copyright © 2011-2022 走看看