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

    A

    发现限制形如选了一条边之后,不能选和它端点相同的边。

    我们建一张新图,新图中的点代表原图中的边,如果两个边不能同时选就连边。首先可以证明问题一定有解:因为这个问题等价于对于每个弱连通分量找出一个环,而只要出度入度都为 (1) 就一定有环,所以这种情况下更有环。

    我们发现原图的一种答案对应了新图的一种二分图染色方式,所以我们可以得到新图的每个连通块都是一个二分图,每个连通块有 (2) 种染色方式,总方案数就是 (2^{C})(C) 是连通块大小。

    #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 = 2e5 + 5;
    const int ha = 998244353;
    int n;
    int f[MAXN];
    std::vector<P> G[MAXN],rG[MAXN];
    
    inline int find(int x){
    	return x == f[x] ? x : f[x] = find(f[x]);
    }
    
    inline void merge(int x,int y){
    	x = find(x);y = find(y);
    	if(x == y) return;
    	f[x] = y;
    }
    
    int main(){
    	scanf("%d",&n);
    	FOR(i,1,2*n) f[i] = i;
    	FOR(i,1,2*n){
    		int u,v;scanf("%d%d",&u,&v);
    		G[u].pb(MP(v,i));
    		rG[v].pb(MP(u,i));
    	}
    	FOR(v,1,n){
    		merge(G[v][0].se,G[v][1].se);
    		for(auto x:G[v]){
    			for(auto y:rG[x.fi]){
    				if(y.se != x.se){
    					merge(x.se,y.se);
    				}
    			}
    		}
    	}
    	int t = 1;
    	FOR(i,1,2*n) if(find(i) == i) t = 2ll*t%ha;
    	printf("%d
    ",t);
    	return 0;
    }
    

    B

    (t_i) 表示交换 (A_i,A_{i+1}) 的时间,如果存在 (a_i=i) 一定无解。

    如果有位置满足 (a_i < i),那么相当于有限制 (t_{a_i} > t_{a_i+1} > ldots > t_{i-1})(a_i > i) 也是类似的。

    可以用差分(或者直接暴力)处理处相邻两个数的限制,现在变成了相邻两个数有限制 < 或者 > ,求排列方案数。

    (f_{i,j}) 表示考虑前 (i) 个数,最后一个数的排名是 (j) 的方案数,前缀和优化转移即可。

    另一种做法是我们考虑容斥,将 > 容斥为 <,那么新的序列形如若干连通块,大小为 (s_1,s_2,ldots,s_m),那么方案就是 (inom {n}{s_1,s_2,ldots,s_m})。可以写个 dp:设 (f_{i}) 表示前缀 (i) 的答案,转移枚举最后一段连续段是啥,算上容斥系数一起转移就好了。可以用分治 NTT 优化。

    #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 = 5000+5;
    const int ha = 1e9 + 7;
    bool vis[MAXN];
    int ans = 1;
    
    inline int qpow(int a,int n=ha-2){
    	int res = 1;
    	while(n){
    		if(n & 1) res = 1ll*res*a%ha;
    		a = 1ll*a*a%ha;
    		n >>= 1;
    	}
    	return res;
    }
    
    int fac[MAXN],inv[MAXN];
    
    inline void prework(){
    	fac[0] = 1;FOR(i,1,MAXN) fac[i] = 1ll*fac[i-1]*i%ha;
    	inv[MAXN-1] = qpow(fac[MAXN-1]);ROF(i,MAXN-2,0) inv[i] = 1ll*inv[i+1]*(i+1)%ha;
    }
    
    inline int C(int n,int m){
    	if(n < 0 || m < 0 || n < m) return 0;
    	return 1ll*fac[n]*inv[m]%ha*inv[n-m]%ha;
    }
    
    int n,a[MAXN];
    char str[MAXN];// i和i+1的关系
    int f[MAXN][MAXN],pre[MAXN],suf[MAXN];
    
    inline void add(int &x,int y){
    	x += y;if(x >= ha) x -= ha;
    }
    
    int main(){
    	prework();scanf("%d",&n);
    	FOR(i,1,n) scanf("%d",a+i),++a[i];
    	FOR(i,1,n){
    		if(a[i] == i){
    			puts("0");exit(0);
    		}
    		if(a[i] < i){
    			FOR(j,a[i],i-2){
    				if(str[j] == '<'){
    					puts("0");
    					exit(0);
    				}
    				str[j] = '>';
    			}
    		}
    		else{
    			FOR(j,i,a[i]-2){
    				if(str[j] == '>'){
    					puts("0");exit(0);
    				}
    				str[j] = '<';
    			}
    		}
    	}
    	f[1][1] = 1;
    	FOR(i,2,n-1){
    		pre[0] = suf[i] = 0;
    		FOR(j,1,i-1) pre[j] = pre[j-1],add(pre[j],f[i-1][j]);
    		ROF(j,i-1,1) suf[j] = suf[j+1],add(suf[j],f[i-1][j]);
    		FOR(j,1,i){
    			if(str[i-1] == '<'){
    				f[i][j] = pre[j-1];
    			}
    			else if(str[i-1] == '>'){
    				f[i][j] = suf[j];
    			}
    			else{
    				f[i][j] = pre[i-1];
    			}
    		}
    	}
    	int ans = 0;
    	FOR(i,1,n-1) add(ans,f[n-1][i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    C

    我们考虑一下不变量:发现任意 (3 imes 3) 的矩阵中,(a_{1,1}+a_{2,3}+a_{3,2}-a_{1,2}-a_{2,1}-a_{3,3}) 是不变的。

    这样的矩阵形如:

    + - 0
    - 0 +
    0 + -
    

    发现无论做什么操作,这个值都不变。

    所以我们只要能把前两行前两列都消成 (0) ,如果满足条件,剩下的格子都能推出来是 (0)

    先考虑如何消两行:从左往右考虑每一列,每次先用列操作消去下面的数,再用对角线操作消去上面的数。

    消两列同理。

    #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 = 1000+5;
    
    int n,m;LL a[MAXN][MAXN];
    
    struct Node{
    	int opt,x;LL k;
    	Node(int opt=0,int x=0,LL k=0) : opt(opt),x(x),k(k) {}
    };
    
    std::vector<Node> S;
    
    inline void gao(int opt,int x,LL k){
    	S.pb(Node(opt,x,k));
    	if(opt == 1){
    		FOR(i,1,m) a[x][i] += k;
    	}
    	else if(opt == 2){
    		FOR(i,1,n) a[i][x] += k;
    	}
    	else{
    		// j-i = x
    		FOR(i,1,n){
    			int j = i+x;
    			if(j >= 1 && j <= m) a[i][j] += k;
    		}
    	}
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	FOR(i,1,n) FOR(j,1,m) scanf("%lld",&a[i][j]);
    	ROF(i,n,1){
    		gao(1,i,-a[i][1]);
    		gao(3,2-i,-a[i][2]);
    	}
    	FOR(i,3,m){
    		gao(2,i,-a[2][i]);
    		gao(3,i-1,-a[1][i]);
    	}
    	bool flag = 1;
    	FOR(i,1,n) FOR(j,1,m) flag &= (a[i][j] == 0);
    	if(!flag) puts("-1");
    	else{
    		printf("%d
    ",(int)S.size());
    		for(auto x:S) printf("%d %d %lld
    ",x.opt,x.x,x.k);
    	}
    	return 0;
    }
    

    D

    考试差一点优化。。

    首先写出 dp 然后是个经典的斜率优化形式就不用说了。

    但是这都不单调,如果直接做是 (O(n^2 log n)) 的。但是我们注意到询问点只有 (O(n)) 个,所以预先对可能的询问点排序,每建完凸包就直接用单调性扫过去回答所有询问即可。(O(n^2+nlog 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 = 5000+5;
    
    struct Node{
    	LL x,y;
    	Node(LL x=0,LL y=0) : x(x),y(y) {}
    	
    	inline Node operator + (const Node &t) const {
    		return Node(x+t.x,y+t.y);
    	}
    	
    	inline Node operator - (const Node &t) const {
    		return Node(x-t.x,y-t.y);
    	}
    	
    	inline LL operator * (const Node &t) const {
    		return x*t.y-y*t.x;
    	}
    	
    	inline bool operator < (const Node &t) const {
    		return x == t.x ? y > t.y : x < t.x;
    	}
    };
    
    int n,a[MAXN];LL sm[MAXN];
    LL f[MAXN][MAXN];
    std::vector<P> S;
    LL w[MAXN][MAXN];
    
    inline LL calc(Node a,LL x){
    	return -a.x*x+a.y;
    }
    
    inline void build(int id){
    	std::vector<Node> v;
    	std::deque<Node> tb;
    	for(auto x:S){
    		if(x.se < id){
    			v.pb(Node(sm[x.se],f[id][x.se]-sm[id]*sm[id]+sm[id]*sm[x.se]));
    		}
    	}
    	std::reverse(all(v));
    	for(auto x:v){
    		while(tb.size() >= 2 && (tb.back()-tb[(int)tb.size()-2])*(x-tb.back()) >= 0) tb.pop_back();
    		tb.pb(x);
    	}
    	for(auto x:S){
    		while(tb.size() >= 2 && calc(tb[0],x.fi) <= calc(tb[1],x.fi)) tb.pop_front();
    		w[id][x.se] = calc(tb[0],x.fi);
    	}
    }
    
    inline void trans(int id){
    	f[id][0] = 0;
    	FOR(i,1,id-1){
    		f[id][i] = w[i][id]+sm[id]*sm[i];
    	}
    }
    
    int main(){
    	scanf("%d",&n);
    	FOR(i,1,n) scanf("%d",a+i),sm[i] = sm[i-1]+a[i];
    	FOR(i,0,n) S.pb(MP(sm[i],i));std::sort(all(S));std::reverse(all(S));
    	f[1][0] = 0;build(1);
    	FOR(i,2,n){
    		trans(i);
    		build(i);
    	}
    	LL ans = 0;
    	FOR(i,0,n-1) ans = std::max(ans,f[n][i]);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    用Java实现四则运算
    敏捷开发角色分配
    需求分析之WBS
    需求分析之NABCD
    电梯演说
    开发流程的选择
    软件团队模式的选择
    维护日程管理项目
    日程管理系统中找错误
    Android的测试
  • 原文地址:https://www.cnblogs.com/rainair/p/14305552.html
Copyright © 2011-2022 走看看