• Codeforces Round #402 (Div. 2) 题解


    Problem A:

    题目大意:

    给定两个数列(a,b),一次操作可以交换分别(a,b)数列中的任意一对数。求最少的交换次数使得任意一个数都在两个序列中出现相同的次数.
    ((1 leq a_i,b_i leq 5 , 1 leq n leq 100))

    题解:

    直觉告诉我这么搞就行了

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
    inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
    const int maxn = 128;
    int a[maxn],b[maxn];
    int numa[8],numb[8];
    int main(){
    	int n;read(n);
    	for(int i=1;i<=n;++i) read(a[i]),numa[a[i]]++;
    	for(int i=1;i<=n;++i) read(b[i]),numb[b[i]]++;
    	int ans = 0;
    	for(int i=1;i<=5;++i){
    		if((numa[i] + numb[i]) % 2 == 1) return puts("-1");
    		ans += abs(numa[i] - numb[i])/2;
    	}printf("%d
    ",ans>>1);
    	getchar();getchar();
    	return 0;
    }
    

    Problem B:

    题目大意:

    给定两个正整数n,k,求n最少的删除数码的次数使其可以被(10^k)整除.

    题解:

    直接删除后k位就好了,我居然栽在这道题上...

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    int main(){
    	string s;int k;
    	cin >> s >> k;
    	reverse(s.begin(),s.end());
    	int len = s.size();
    	int cnt1 = 0,cnt2 = 0;
    	for(int i=0;i<len;++i){
    		if(s[i] == '0') ++ cnt1;
    		else ++ cnt2;
    		if(cnt1 == k) break;
    	}
    	if(cnt1 == k) printf("%d
    ",cnt2);
    	else printf("%d
    ",len-1);
    	getchar();getchar();
    	return 0;
    }
    

    Problem C

    题目大意:

    有n个物品一定要购买,给出两个数组a,b分别表示这个星期和下个星期时物品的价值,规定这个星期必须买至少k件物品,求最小的代价买到所有物品.

    题解:

    首先我们在这个星期买掉所有的物品
    然后我们到了下一个星期,如果发现有什么物品更便宜了
    那么我们可以让时光倒流回上个星期,并且选择不在上个星期购买这个物品
    至于k的限制吗。。。去价值差大于零的值中的前n-k大即可.

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
    inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
    priority_queue<int> q;
    const int maxn = 200010;
    int a[maxn],b[maxn];
    int main(){
    	int n,k;read(n);read(k);
    	ll sum=0;
    	for(int i=1;i<=n;++i) read(a[i]),sum+=a[i];
    	for(int i=1;i<=n;++i){
    		read(b[i]);
    		q.push(a[i]-b[i]);
    	}
    	for(int i=k+1;i<=n;++i){
    		if(q.top()>0) sum-=q.top(),q.pop();
    	}
    	printf("%I64d
    ",sum);
    	getchar();getchar();
    	return 0;
    }
    

    Problem D

    题目大意:

    给定两个字符串S,T,设第一个字符串的长度为n,再给定一个{n}的排列。然后按照给定的排列依次删除S中对应位置上的字符.问第几次删除的时候T不再是S的子序列.

    题解:

    首先我们可以发现答案具有明显的单调性
    所以我们二分答案
    然后直接扫一遍两个串判定即可

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
    inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
    const int maxn = 200010;
    char s1[maxn],s2[maxn];
    int a[maxn],n,m;
    bool vis[maxn];
    inline bool check(int mid){
    	memset(vis,0,sizeof vis);
    	for(int i=1;i<=mid;++i){
    		vis[a[i]] = true;
    	}
    	int p1 = 1,p2 = 1;
    	while(p2 <= m && p1 <= n){
    		if(vis[p1]) ++ p1;
    		else{
    			if(s2[p2] == s1[p1]) ++p1,++p2;
    			else ++ p1;
    		}
    	}
    	if(p2 == m+1) return true;
    	return false;
    }
    int main(){
    	scanf("%s%s",s1+1,s2+1);
    	n = strlen(s1+1);
    	m = strlen(s2+1);
    	for(int i=1;i<=n;++i){
    		read(a[i]);
    	}
    	int l = 0,r = n,ans = -1;
    	while(l <= r){
    		int mid = (l+r) >> 1;
    		if(check(mid)) ans = mid,l = mid+1;
    		else r = mid-1;
    	}printf("%d
    ",ans);
    	getchar();getchar();
    	return 0;
    }
    

    Problem E

    题目大意:

    给定一个只有赋值和二元位运算的程序,并且其中存在未知的变量'?',要求为所有的变量'?'确定一个相同的值.使程序结束后所有变量的和达到最值。
    分别输出达到最大值和最小值时的'?'的值.

    题解:

    这是一道考码力的题啊。。。
    分别枚举'?'的每个二进制位,下去计算变量之和即可

    #include <map>
    #include <string>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
    inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
    const int maxn = 5010;
    map<string,int> mp;
    char vari[maxn][maxn],va[maxn][maxn];
    int typ[maxn],tmpx[maxn],tmpy[maxn],ans1[maxn],ans2[maxn];
    char tmp[maxn],tt[maxn];
    int n,m,val[maxn];
    int work(int x,int t){
    	val[0] = t;
    	for(int i=1;i<=n;++i){
    		if(typ[i] == 0) val[i] = va[i][x] - '0';
    		else if(typ[i] == 1) val[i] = val[tmpx[i]] ^ val[tmpy[i]];
    		else if(typ[i]==2) val[i] = val[tmpx[i]] | val[tmpy[i]];
    		else val[i] = val[tmpx[i]] & val[tmpy[i]];
    	}int sum = 0;
    	for(int i=1;i<=n;++i) sum += val[i];
    	return sum;
    }
    int main(){
    	read(n);read(m);mp["?"] = 0;
    	for(int i=1;i<=n;++i){
    		scanf("%s",vari[i]);
    		scanf("%s%s",tt,va[i]+1);
    		mp[vari[i]]=i;
    		if(va[i][1] == '0'||va[i][1] == '1'){
    			typ[i] = 0;
    			continue;
    		}
    		tmpx[i] = mp[va[i]+1];
    		scanf("%s",tmp);
    		if(tmp[0] == 'X') typ[i] = 1;
    		else if(tmp[0] == 'O') typ[i] = 2;
    		else typ[i] = 3;
    		scanf("%s",va[i]+1);
    		tmpy[i] = mp[va[i]+1];
    	}
    	for(int i=1;i<=m;++i){
    		int sum1 = work(i,1);
    		int sum0 = work(i,0);
    		if(sum1 < sum0) ans1[i] = 1;else ans1[i]=0;
    		if(sum1 > sum0) ans2[i] = 1;else ans2[i] = 0;
    	}
    	for(int i=1;i<=m;++i) printf("%d",ans1[i]);puts("");
    	for(int i=1;i<=m;++i) printf("%d",ans2[i]);puts("");
    	getchar();getchar();
    	return 0;
    }
    

    Problem F

    题目大意:

    给定你一颗若干个串插入得到的Trie树,并且不存在任意一个串是另一个串的前缀.现在允许你删除所有长度>=k字符串的下标为k的字符,然后再把这些串插入到Trie中.确定一个尽量小的k使得后来得到的Trie的节点数目最少.(首先保证节点数目最少)

    题解:

    好长的题面...
    我们发现其实就是让我们删除Trie树中的一层,然后把剩余的部分该合并的合并
    使得最终的Trie的size最小,并且多解的情况下输出最小的k
    我们其实可以直接计算出每一层如果删去了,那么会合并+删除多少个节点
    然后我们遍历每一层取一个最大值即可
    至于删去每一层时的计算,我们可以直接递归处理.
    (比赛的时候没做出来就是因为只考虑了两层...)
    其中有一个很关键的优化,如果遇到了一个点只有一个儿子的情况则直接退出
    这样就保证了单次计算复杂度最高是(logn)的.
    所以总复杂度(O(nlogn))就可以AC了
    现在想想自己当时比赛的时候真是傻了

    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    const int maxn = 300010;
    int ch[maxn][27];
    #define id(x) (x - 'a')
    int dep[maxn],mxp,anss[maxn];
    int get_num(const vector<int> &v){
    	int ret = v.size() - 1;
    	int n = v.size();
    	vector<int> vx;
    	for(int i='a';i<='z';++i){
    		vx.clear();
    		for(int j=0;j<n;++j){
    			if(ch[v[j]][id(i)]) vx.push_back(ch[v[j]][id(i)]);
    		}if(vx.size() <= 1) continue;
    		ret += get_num(vx);
    	}return ret;
    }
    void dfs(int u,int dep){
    	mxp = max(mxp,dep);
    	vector<int> v;
    	for(int i = 'a';i<= 'z';++i){
    		if(ch[u][id(i)]) v.push_back(ch[u][id(i)]);
    	}anss[dep] += get_num(v) + 1;
    	for(int i = 'a';i<= 'z';++i){
    		if(ch[u][id(i)]) dfs(ch[u][id(i)],dep+1);
    	}return;
    }
    int main(){
    	int n;read(n);
    	char c;
    	for(int i=1,u,v;i<n;++i){
    		read(u);read(v);while(c=getchar(),c<'!');
    		ch[u][id(c)] = v;
    	}dfs(1,1);
    	int ans = 0,ans_p = -1;
    	for(int i=1;i<=mxp;++i){
    		if(ans < anss[i]){
    			ans = anss[i];
    			ans_p = i;
    		}
    	}
    	printf("%d
    %d
    ",n-ans,ans_p);
    	getchar();getchar();
    	return 0;
    }
    

    抽空去做做Div.1的D和E....

  • 相关阅读:
    第三次博客作业
    多项式求导--三次作业小结
    Python实现批量修改文件名
    汉字编程 —— 第一次个人编程作业
    PAT甲级代码仓库
    谈谈自己 —— 第一次博客作业
    爬取豆瓣网图书TOP250的信息
    HDU1862
    HDU1408
    HDU1302
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6476162.html
Copyright © 2020-2023  润新知