zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 62

    最近省队前联考被杭二成七南外什么的吊锤得布星,拿一场Div. 2恢复信心

    然后Div.2 Rk3、Div. 1+Div. 2 Rk9,rating大涨200引起舒适

    现在的Div. 2都怎么了,最难题难度都快接近3K了……

    A. Detective Book

    (a_i)的前缀最大值为(Max_i),那么要求的就是(Max_i = i)(i)的数量

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<ctime>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    #include<iomanip>
    #include<queue>
    #include<map>
    #include<set>
    #include<bitset>
    #include<vector>
    #include<cmath>
    #include<random>
    //This code is written by Itst
    using namespace std;
    
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	bool f = 0;
    	while(!isdigit(c) && c != EOF){
    		if(c == '-')
    			f = 1;
    		c = getchar();
    	}
    	if(c == EOF)
    		exit(0);
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return f ? -a : a;
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    //freopen("out","w",stdout);
    #endif
    	int N = read() , maxN = 0 , cnt = 0;
    	for(int i = 1 ; i <= N ; ++i){
    		maxN = max(maxN , read());
    		if(maxN == i) ++cnt;
    	}
    	cout << cnt;
    	return 0;
    }
    

    B. Good String

    注意到如果字符串最右侧是一个'<'或者最左侧是一个'>'的话,我们只需要一直对最右边或者最左边的字符进行操作就可以满足条件

    所以我们只需要在字符串首尾删去尽可能少的字符使得其最右侧是一个‘<’或者最左侧是一个'>'。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<ctime>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    #include<iomanip>
    #include<queue>
    #include<map>
    #include<set>
    #include<bitset>
    #include<vector>
    #include<cmath>
    #include<random>
    //This code is written by Itst
    using namespace std;
    
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	bool f = 0;
    	while(!isdigit(c) && c != EOF){
    		if(c == '-')
    			f = 1;
    		c = getchar();
    	}
    	if(c == EOF)
    		exit(0);
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return f ? -a : a;
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    	//freopen("out","w",stdout);
    #endif
    	int T;
    	for(cin >> T ; T ; --T){
    		string s;
    		int len;
    		cin >> len >> s;
    		if(s[0] == '<' && s[s.size() - 1] == '>'){
    			int cnt1 = 0 , cnt2 = 0;
    			for(int i = 0 ; i < s.size() && s[i] == '<' ; ++i)
    				++cnt1;
    			for(int i = s.size() - 1 ; i >= 0 && s[i] == '>' ; --i)
    				++cnt2;
    			cout << min(cnt1 , cnt2) << endl;
    		}
    		else cout << 0 << endl;
    	
    	}
    	return 0;
    }
    

    C. Playlist

    从大到小枚举选择的乐曲中最小的愉悦值,用一个堆维护愉悦值大于等于当前枚举值的所有乐曲的播放时间前(k)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<ctime>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    #include<iomanip>
    #include<queue>
    #include<map>
    #include<set>
    #include<bitset>
    #include<vector>
    #include<cmath>
    #include<random>
    //This code is written by Itst
    using namespace std;
    
    #define int long long
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	bool f = 0;
    	while(!isdigit(c) && c != EOF){
    		if(c == '-')
    			f = 1;
    		c = getchar();
    	}
    	if(c == EOF)
    		exit(0);
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return f ? -a : a;
    }
    #define PII pair < int , int >
    #define st first
    #define nd second
    
    bool p(PII a , PII b){return a.st > b.st;}
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    	//freopen("out","w",stdout);
    #endif
    	int N , K;
    	cin >> N >> K;
    	vector < PII > song;
    	priority_queue < int , vector < int > , greater < int > > q;
    	int sum = 0 , ans = 0;
    	for(int i = 1 ; i <= N ; ++i){
    		int k = read() , b = read();
    		song.push_back(PII(b , k));
    	}
    	sort(song.begin() , song.end() , p);
    	for(auto t : song){
    		q.push(t.nd);
    		sum += t.nd;
    		if(q.size() > K){
    			sum -= q.top();
    			q.pop();
    		}
    		ans = max(ans , t.st * sum);
    	}
    	cout << ans;
    	return 0;
    }
    

    D. Minimum Triangulation

    可以证明最优的划分方案中,(1)与所有点都有一条边。所以直接计算(sumlimits_{i=2}^{n-1} i imes (i+1))即可

    我觉得可以直接从样例看出这个结论

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<ctime>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    #include<iomanip>
    #include<queue>
    #include<map>
    #include<set>
    #include<bitset>
    #include<vector>
    #include<cmath>
    #include<random>
    //This code is written by Itst
    using namespace std;
    
    #define int long long
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	bool f = 0;
    	while(!isdigit(c) && c != EOF){
    		if(c == '-')
    			f = 1;
    		c = getchar();
    	}
    	if(c == EOF)
    		exit(0);
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return f ? -a : a;
    }
    #define PII pair < int , int >
    #define st first
    #define nd second
    
    bool p(PII a , PII b){return a.st > b.st;}
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    	//freopen("out","w",stdout);
    #endif
    	int N , sum = 0;
    	cin >> N;
    	for(int i = 3 ; i <= N ; ++i)
    		sum = sum + i * (i - 1);
    	cout << sum;
    	return 0;
    }
    

    E. Palindrome-less Arrays

    既然不能存在长度(>1)的奇回文串,那么一定不能存在长度为(3)的回文串,也就意味着(forall i in [1,n-2] , a_i eq a_{i+2})

    把下标为奇数和下标为偶数的(a_i)拿出来作为两个序列(b_i,c_i),那么我们只需要(b_i eq b_{i+1} , c_i eq c_{i+1})就是合法的方案

    对于两个序列分别dp,设(f_{i,0/1})表示当前已经填完前(i)个数,且第(i)个数的值与(i)之后的第一个有限制的位置的值是否相等的方案数,转移分有限制的点和无限制的点。

    边界情况有点多需要注意。

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    //This code is written by Itst
    using namespace std;
    
    #define int long long
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	bool f = 0;
    	while(!isdigit(c) && c != EOF){
    		if(c == '-')
    			f = 1;
    		c = getchar();
    	}
    	if(c == EOF)
    		exit(0);
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return f ? -a : a;
    }
    
    const int MAXN = 2e5 + 7 , MOD = 998244353;
    int dp[MAXN][2] , arr[MAXN] , N , K;
    
    inline int poww(int a , int b){
    	int times = 1;
    	while(b){
    		if(b & 1) times = times * a % MOD;
    		a = a * a % MOD;
    		b >>= 1;
    	}
    	return times;
    }
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    	//freopen("out","w",stdout);
    #endif
    	N = read(); K = read();
    	for(int i = 1 ; i <= N ; ++i)
    		arr[i] = read();
    	vector < int > ind;
    	ind.clear();
    	for(int i = 1 ; i <= N ; i += 2)
    		if(arr[i] != -1)
    			ind.push_back((i + 1) >> 1);
    	int times1 , times2;
    	if(!ind.empty()){
    		dp[0][0] = 1;
    		int pos = 0;
    		for(int i = 1 ; pos < ind.size() ; ++i)
    			if(ind[pos] == i){
    				dp[i][0] = dp[i - 1][0];
    				if(++pos < ind.size() && arr[ind[pos] * 2 - 1] == arr[ind[pos - 1] * 2 - 1])
    					swap(dp[i][0] , dp[i][1]);
    			}
    			else{
    				if(i == 1){
    					dp[i][0] = K - 1; dp[i][1] = 1;
    				}
    				else{
    					dp[i][0] = (dp[i - 1][0] * (K - 2) + dp[i - 1][1] * (K - 1)) % MOD;
    					dp[i][1] = dp[i - 1][0];
    				}
    			}
    		--pos;
    		times1 = poww(K - 1 , (N + 1) / 2 - ind[pos]) * (dp[ind[pos]][0] + dp[ind[pos]][1]) % MOD;
    	}
    	else
    		times1 = K * poww(K - 1 , (N + 1) / 2 - 1) % MOD;
    	ind.clear();
    	for(int i = 2 ; i <= N ; i += 2)
    		if(arr[i] != -1)
    			ind.push_back(i >> 1);
    	if(!ind.empty()){
    		memset(dp , 0 , sizeof(dp));
    		dp[0][0] = 1;
    		int pos = 0;
    		for(int i = 1 ; pos < ind.size() ; ++i)
    			if(ind[pos] == i){
    				dp[i][0] = dp[i - 1][0];
    				if(++pos < ind.size() && arr[ind[pos] * 2] == arr[ind[pos - 1] * 2])
    					swap(dp[i][0] , dp[i][1]);
    			}
    			else{
    				if(i == 1){
    					dp[i][0] = K - 1; dp[i][1] = 1;
    				}
    				else{
    					dp[i][0] = (dp[i - 1][0] * (K - 2) + dp[i - 1][1] * (K - 1)) % MOD;
    					dp[i][1] = dp[i - 1][0];
    				}
    			}
    		--pos;
    		times2 = poww(K - 1 , N / 2 - ind[pos]) * (dp[ind[pos]][0] + dp[ind[pos]][1]) % MOD;
    	}
    	else
    		times2 = K * poww(K - 1 , N / 2 - 1) % MOD;
    	cout << times1 * times2 % MOD;
    	return 0;
    }
    

    F. Extending Set of Points

    如果你做过eJOI2018 元素周期表,这道题应该不难想到怎么做

    对于题目中"((x_1 , y_1) in R , (x_1 , y_2) in R , (x_2 , y_1) in R , (x_2 , y_2) eq R)时将((x_2,y_2))加入点集中"的操作,可以使用并查集描述:

    并查集中存(6 imes 10^5)个点分别表示行和列,对于一个点((x,y)),将第(x)行和第(y)列在并查集中合并。注意到加入((x_1 , y_1)(x_1 , y_2)(x_2 , y_1))之后,(x_2)行和(y_2)列就在同一个并查集里了,这就表示((x_2,y_2))成为了点集(R)中的一个点。不难得到加完所有点后,答案是所有并查集包含的行数和列数的乘积之和。

    注意到要删点,所以使用线段树分治+带撤销并查集做一下就可以了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<stack>
    #include<vector>
    //This code is written by Itst
    using namespace std;
    
    #define int long long
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	bool f = 0;
    	while(!isdigit(c) && c != EOF){
    		if(c == '-')
    			f = 1;
    		c = getchar();
    	}
    	if(c == EOF)
    		exit(0);
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return f ? -a : a;
    }
    
    const int MAXN = 6e5 + 7;
    #define PII pair < int , int >
    #define st first
    #define nd second
    map < PII , int > mp;
    vector < PII > Edge[MAXN << 2];
    int fa[MAXN] , szX[MAXN] , szY[MAXN] , Q , ans;
    
    int find(int x){
    	return fa[x] == x ? x : find(fa[x]);
    }
    
    #define mid ((l + r) >> 1)
    #define lch (x << 1)
    #define rch (x << 1 | 1)
    void addEdge(int x , int l , int r , int L , int R , PII pos){
    	if(l >= L && r <= R){
    		Edge[x].push_back(pos);
    		return;
    	}
    	if(mid >= L) addEdge(lch , l , mid , L , R , pos);
    	if(mid < R) addEdge(rch , mid + 1 , r , L , R , pos);
    }
    
    void merge(int x , int y , stack < int > &stk){
    	x = find(x); y = find(y);
    	if(x == y) return;
    	ans -= szX[x] * szY[x] + szX[y] * szY[y];
    	if(szX[x] + szY[x] < szX[y] + szY[y])
    		swap(x , y);
    	fa[y] = x; szY[x] += szY[y]; szX[x] += szX[y];
    	ans += szX[x] * szY[x];
    	stk.push(y);
    }
    
    void work(int x , int l , int r){
    	stack < int > stk;
    	int lastans = ans;
    	for(auto t : Edge[x])
    		merge(t.st , t.nd + 300000 , stk);
    	if(l == r)
    		printf("%lld " , ans);
    	else{
    		work(lch , l , mid);
    		work(rch , mid + 1 , r);
    	}
    	while(!stk.empty()){
    		int t = stk.top(); stk.pop();
    		int p = find(t);
    		szX[p] -= szX[t]; szY[p] -= szY[t];
    		fa[t] = t;
    	}
    	ans = lastans;
    }
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    	//freopen("out","w",stdout);
    #endif
    	Q = read();
    	for(int i = 1 ; i <= Q ; ++i){
    		int a = read() , b = read();
    		PII t = PII(a , b);
    		if(mp.find(t) == mp.end()) mp[t] = i;
    		else{
    			addEdge(1 , 1 , Q , mp[t] , i - 1 , t);
    			mp.erase(mp.find(t));
    		}
    	}
    	for(auto t : mp)
    		addEdge(1 , 1 , Q , t.second , Q , t.first);
    	for(int i = 1 ; i <= 3e5 ; ++i)
    		szX[fa[i] = i] = 1;
    	for(int i = 3e5 + 1 ; i <= 6e5 ; ++i)
    		szY[fa[i] = i] = 1;
    	work(1 , 1 , Q);
    	return 0;
    }
    

    G. Double Tree

    考虑转移问题的优先级:我们要求((u,v)(2 | u , 2|v))((u,v)(2 otmid u , 2 otmid v))的总经过次数最小,其次总边权最小。

    既然两棵树同构,可以把这两棵树拍扁到一起变成一棵树,那么我们需要在这一棵树上经过边数最小,那么经过的显然是两点之间的路径。

    上面的问题可以在拍扁的树上树形DP+倍增处理:设(f_{i,j,k=0/1,l=0/1})表示:设从(i)开始跳(2^j)次方步到达的点为(x),那么原图中((2 imes i + k , 2 imes x + l))的最短路是多少。注意上面的(i,x)指的是在拍扁的树上的编号。转移类似于矩阵乘法。

    注意到转移优先级之后有可能答案不优,因为可能存在某些情况会绕一段路到另一棵树上,边权总和比直接走的边权要小。如果能将所有((2i - 1 , 2i))的边权变为(2i-1)(2i)之间的最短路长度,那么上面的情况就会覆盖((2i - 1 , 2i))的边权,在DP过程中就不需要考虑了。

    所以最后的问题是如何求出所有(2i-1)(2i)的最短路。有一个很精妙的SSSP做法:建(N+1)个点编号为(0)(N),连边((0,i,w_{2i-1,2i})),对于树上存在的边((i,j,w_1,w_2))在图上连((i,j,w_1+w_2)),跑Dijkstra,得到(0)(i)的最短路长度就是(2i-1)(2i)的最短路长度。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    //This code is written by Itst
    using namespace std;
    
    #define int long long
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	while(!isdigit(c))
    		c = getchar();
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return a;
    }
    
    const int MAXN = 3e5 + 7;
    int N , Q;
    struct Edge{int end , upEd , w;};
    
    inline void addEd(Edge *Ed , int *head , int &cntEd , int a , int b , int c , bool f = 0){
    	Ed[++cntEd] = (Edge){b , head[a] , c};
    	head[a] = cntEd;
    	if(f){
    		Ed[++cntEd] = (Edge){a , head[b] , c};
    		head[b] = cntEd;
    	}
    }
    
    namespace SSSP{
    #define PII pair < int , int >
    #define st first
    #define nd second
    	Edge Ed[MAXN << 2];
    	int head[MAXN] , dis[MAXN];
    	int cntEd;
    	priority_queue < PII > q;
    	
    	void work(){
    		memset(dis , 0x7f , sizeof(dis));
    		dis[0] = 0;
    		q.push(PII(0 , 0));
    		while(!q.empty()){
    			PII t = q.top(); q.pop();
    			if(dis[t.nd] != -t.st) continue;
    			for(int i = head[t.nd] ; i ; i = Ed[i].upEd)
    				if(dis[t.nd] + Ed[i].w < dis[Ed[i].end])
    					q.push(PII(-(dis[Ed[i].end] = dis[t.nd] + Ed[i].w) , Ed[i].end));
    		}
    	}
    }
    
    namespace Tree{
        Edge Ed[MAXN << 1];
    	struct matrix{
    		int a[2][2];
    		matrix(){memset(a , 0x3f , sizeof(a));}
    		int* operator [](int x){return a[x];}
    		matrix operator *(matrix b){
    			matrix c;
    			for(int i = 0 ; i < 2 ; ++i)
    				for(int j = 0 ; j < 2 ; ++j)
    					for(int k = 0 ; k < 2 ; ++k)
    						c[j][k] = min(c[j][k] , a[j][i] + b[i][k]);
    			return c;
    		}
    	}dis[MAXN][20] , tmp , initial , toX , toY;
    	int head[MAXN] , dep[MAXN] , fa[MAXN][20] , val2[MAXN << 1] , cntEd;
    	
    	void dfs(int x , int p , matrix val){
    		dis[x][0] = val;
    		fa[x][0] = p;
    		for(int i = 1 ; fa[x][i - 1] ; ++i){
    			fa[x][i] = fa[fa[x][i - 1]][i - 1];
    			dis[x][i] = dis[x][i - 1] * dis[fa[x][i - 1]][i - 1];
    		}
    		dep[x] = dep[p] + 1;
    		for(int i = head[x] ; i ; i = Ed[i].upEd)
    			if(Ed[i].end != p){
    				tmp[1][0] = min(Ed[i].w + SSSP::dis[Ed[i].end] , val2[i] + SSSP::dis[x]);
    				tmp[0][1] = min(val2[i] + SSSP::dis[Ed[i].end] , Ed[i].w + SSSP::dis[x]);
    				tmp[0][0] = min(Ed[i].w , tmp[0][1] + SSSP::dis[x]);
    				tmp[1][1] = min(val2[i] , tmp[1][0] + SSSP::dis[x]);
    				dfs(Ed[i].end , x , tmp);
    			}
    	}
    	
    	void init(){
    		memset(initial.a , 0 , sizeof(initial));
    		dfs(1 , 0 , initial);
    	}
    	
    	int jump(int x , int y , bool f1 , bool f2){
    		if(dep[x] < dep[y]){
    			x ^= y ^= x ^= y;
    			swap(f1 , f2);
    		}
    		toX = initial; toY = initial;
    		toX[0][1] = toX[1][0] = SSSP::dis[x];
    		toY[0][1] = toY[1][0] = SSSP::dis[y];
    		for(int i = 18 ; i >= 0 ; --i)
    			if(dep[x] - (1 << i) >= dep[y]){
    				toX = toX * dis[x][i];
    				x = fa[x][i];
    			}
    		if(x == y) return toX[f1][f2];
    		for(int i = 18 ; i >= 0 ; --i)
    			if(fa[x][i] != fa[y][i]){
    				toX = toX * dis[x][i];
    				toY = toY * dis[y][i];
    				x = fa[x][i]; y = fa[y][i];
    			}
    		toX = toX * dis[x][0]; toY = toY * dis[y][0];
    		return min(toX[f1][0] + toY[f2][0] , toX[f1][1] + toY[f2][1]);
    	}
    	
    	void work(int x , int y){
    		bool f1 = !(x & 1) , f2 = !(y & 1);
    		x = (x + 1) >> 1; y = (y + 1) >> 1;
    		printf("%lld
    " , jump(x , y , f1 , f2));
    	}
    }
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    	freopen("out","w",stdout);
    #endif
    	N = read();
    	for(int i = 1 ; i <= N ; ++i)
    		addEd(SSSP::Ed , SSSP::head , SSSP::cntEd , 0 , i , read());
    	for(int i = 1 ; i < N ; ++i){
    		int a = read() , b = read() , w1 = read() , w2 = read();
    		addEd(SSSP::Ed , SSSP::head , SSSP::cntEd , a , b , w1 + w2 , 1);
    		addEd(Tree::Ed , Tree::head , Tree::cntEd , a , b , w1 , 1);
    		Tree::val2[Tree::cntEd] = Tree::val2[Tree::cntEd - 1] = w2;
    	}
    	SSSP::work(); Tree::init();
    	for(int Q = read() ; Q ; --Q)
    		Tree::work(read() , read());
    	return 0;
    }
    
  • 相关阅读:
    gradle添加阿里云maven库
    来谈谈MySQL的临时表,到底是个什么东西,以及怎么样产生的
    MySQL优化相关参数--先做个记录,以后可能用得到
    对于join操作,MySQL它是咋做的?
    Linux-常用命令记录
    有时候我们自认为有用的索引却并没有被MySQL选择使用?
    C#趟坑: Wait()线程结束时,会忽略子线程
    初次使用Windbg检查C#程序内存
    性能优化之三:将Dottrace过程加入持续集成
    性能优化之二:结构体类型的性能优化
  • 原文地址:https://www.cnblogs.com/Itst/p/10585302.html
Copyright © 2011-2022 走看看