Codeforces Round #694 (Div. 2)ABCDF题解

A. Strange Partition

思路:

最小:n个数先求和,再除以x
最大:n个数先分别除以x,再求和

#include<bits/stdc++.h>
#define LL long long
#define mem(f, x) memset(f,x,sizeof(f)) 
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x)
#define fo(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
using namespace std;
template<class T>inline void read(T &x){
    x=0;register char c=getchar();register bool f=0;
    while(!isdigit(c))f^=c=='-',c=getchar();
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
}
int main(){
	int t;
	cin>>t;
	int n,x,k;
	while(t--){
		LL sum1=0,sum2=0;
		cin>>n>>x;
		for(int i=0;i<n;i++){
			cin>>k;
			if(k%x==0){
				sum1+=k/x;
			}
			else sum1+=k/x+1;
			sum2+=k;//最小值 
		}
		if(sum2%x==0)sum2=sum2/x;
		else sum2=sum2/x+1;
		cout<<sum2<<" "<<sum1<<endl;
	}
	return 0;
}

B. Strange List

思路:

刚理解错题意了,题目问的是最终数组所有数的和,我理解成了要计算:当 robot停止的时候,robot扫过的数字和,这显然难度太大,因为你要考虑被分解后得到的数最后是否要被计算到sum里,归结原因,还是自己经验太少,英文题意理解的一知半解,甚至样例解释都没看完就去推公式了

正解:插入x个q/x就是加q,只要q能被x整除就加q,一直到出现一个不能被x整除的数为止,就停止,计算技巧: 多设置一个数组b[]存保存初始值a[],初始值a[]不断变化,但sum加的值一直为初始值b[]的值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100010], b[100010];
template<class T>inline void read(T &x){
    x=0;register char c=getchar();register bool f=0;
    while(!isdigit(c))f^=c=='-',c=getchar();
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
}
int main(){
    ll t, n, x, i;
	read(t);
    while (t--){
        int flag = 0;
        ll sum = 0;
        read(n);read(x);
        for (i = 0; i < n; i++)
            read(a[i]), b[i] = a[i], sum += b[i];
        while (1){
            for (i = 0; i < n; i++){
                if (a[i] % x){
                    flag = 1;
                    break;
                }
                sum += b[i], a[i] /= x;
            }
            if (flag) break;
        }
        cout << sum << endl;
    }
}

C. Strange Birthday Party

思路:

贪心,注意题干说了c序列升序即可

#include<bits/stdc++.h>
using namespace std;
int k[300010], c[300010];
int main(){
    int t, n, m, i, j;cin >> t;
    while (t--){
        long long sum = 0;
        cin >> n >> m;
        for (i = 1; i <= n; i++)cin >> k[i];//限定范围 
        for (i = 1; i <= m; i++)cin >> c[i];//cost
        sort(k + 1, k + n + 1);
        int pn = 0;//设立一个对c的指针,O(n)扫完
        int mi = c[++pn];//最便宜的 
        for (i = n; i >= 1; i--){//k大的先选 
            if (mi < c[k[i]]) sum += mi, mi = c[++pn];
            else sum += c[k[i]];
        }
        cout << sum << endl;
    }
}

D. Strange Definition

思路:

可以把每个数字归结为某个数字,如果这个数字出现次数是奇数,最后一定是最小的不能除以完全平方数字的数字,如果是偶数,那么最后可以规约为1.然后判断w是否为0输出不同情况即可。

  • 如果一个组内元素个数(这个数字出现次数)是偶数,那么经过一秒后,所有这样的小组就可以合成一组。
  • 如果组内元素个数是奇数,经过一秒后,它们不会改变,即无法合成一组

1秒及之后的时间情况都是和1秒的时候相同的,答案只要考虑0秒和1秒及之后两种情况。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int const N = 3e5 + 10;
int n, q, T;
unordered_map<int, int> mp;
void solve(int x) {
    for (int i = 2;; i++) {
        int two = i * i;
        while (x % two == 0 && x >= two) x /= two;
 
        if (x < two) break;
    }
    mp[x]++;
    //x的出现次数和是x否是1,是我们关心的重点 
}
int main(){
    scanf("%d", &T);
    while (T--) {
        mp.clear();
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            int a;
            scanf("%d", &a);
            solve(a);
        }
        int ans1 = 0;
        int ans2 = 0;
        for (auto it = mp.begin(); it !=mp.end();it++){
            ans1 = max(ans1, it->second);
            //cout << it->first << " " << it->second << endl;
            //x出现次数是偶数的这些集合们在>=1s后就会合并到一起 
            //如果x是1,这些集合也会合并到一起
            //这两个判断条件不能分开写,不然会WA 
            if (it->second % 2 == 0 || it->first == 1) ans2 += it->second;
        }
        ans2 = max(ans2, ans1);//把奇数部分考虑进去
        scanf("%d", &q);
        for (int i = 0; i < q; i++) {
            LL t;
            scanf("%lld", &t);
            if (t == 0)
                cout << ans1 << endl;
            else
                cout << ans2 << endl;
        }
    }
    return 0;
}

F. Strange Housing

思路:

可以证明如果图是连通图,那么一定存在一种方法使得所有条件成立。因为如果你使用染色法对特殊点染色,一个点周围如果存在特殊点,那么这个点一定不能是特殊点,然后一定可以将所有的点染色。

判断连通用并查集,染色用dfs,详见代码。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
vector<int> adj[N];//邻接表
int n, m, vis[N], f[N], col[N];

int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }

void dfs(int u, int fa){//dfs染色
    vis[u] = 1, col[u] = !col[fa];//首先颜色和父亲不同 
    
    for (auto v : adj[u])
    //如果我之前访问了这个点,且这个点已经被染色了,那么我肯定和它不同 
        if (vis[v]) col[u] &= !col[v];
        
    //如果我之没访问这个点,且这个点不是父亲,那么我dfs访问这个点 
    for (auto v : adj[u])
        if (!vis[v] && v != fa) 
            dfs(v, u);        
}

int main() {
    int T;
    cin >> T;
    while (T--) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) {//各种初始化 
            vis[i] = 0, f[i] = i;
            adj[i].clear(), col[i] = 0;
        }
        int comp = n;
        for (int i = 1,u,v; i <= m; i++){
            scanf("%d%d", &u, &v);
            if (find(u) != find(v)) comp--, f[find(u)] = find(v);//并查集判断连通 
            adj[u].push_back(v), adj[v].push_back(u);//邻接表建图 
        }
        if (comp != 1) {//如果不连通 
            puts("NO");
            continue;
        }
        puts("YES");
        dfs(1, 0);
        int cnt = 0;
        vector<int> ans;
        for (int i = 1; i <= n; i++)
            if (col[i]) ans.push_back(i), cnt++;//找被染色的点 
        printf("%d\n", cnt);
        for (auto v : ans) printf("%d ", v);
        puts("");
    }
    return 0;
}

借鉴好文:

【Codeforces Round #694 (Div. 2)】D.Strange Definition

posted @ 2021-01-08 22:02  chenshunpeng  阅读(139)  评论(0)    收藏  举报