Codeforces Round 955 (Div. 2, with prizes from NEAR!)

A. Soccer

题意:给定开始前两个分数和结束时两个分数,问有没有可能在这段期间两个队伍从来没有过分数相等的情况。(每次得分只能+1)

思路:确定了两种一定有分数相等的情况(一对之前的分数较低,但是后面的分数较高),其他就是可能没有出现分数相等的情况。

void solve() { 
	int a, b, c, d;
	cin >> a >> b >> c >> d;

	if ((a < b && c >= d) || (a > b && c <= d)){
		cout << "NO\n";
	}
	else{
		cout << "YES\n";
	}
}

B. Collatz Conjecture

题意:给定x,y和k,每次操作必须先增加x,然后判断y是否是x的除数,是的话就一直除,k轮操作后x是多少。

思路:y最小为2,假设x是y的幂,那么运算次数不会超过log(x)次。
每次操作,求出y要成为x的因子,x需要增加多少。其次在运算过程中,如果出现了x为1的情况,那么此时的最后x结果就是1 + (k % (y - 1))。

总结:如果x=1了,那么接下来肯定只考虑k就行。关键是如何考虑?x肯定是先加到y,然后除以y = 1,在这个过程中,每被y整除1次,x就获得1的buff,此时获得的buff应该是k / x,但是k / x 可能是>y的呢? 那么还要继续进行这个操作,有点套娃的感觉,赛时卡在这里了。 其实只要对让k对y - 1取模就行了。这样在数学逻辑上等价于除以y后再判定剩下的数是否能继续被y整除了。而且被y取模有个好处是保证取模后的值<y,那么再加上x的数值1,就是最后答案了,也不需要再考虑这个答案是否会>= y。
这个应该还是数论中的一小点点思维题,奈何数学实在是太差,哎,需要好好学习一下数学了。

其实在x=1时还有种理解方法,此时x + (y - 1)就是y的整数倍了,然后/y又得到了1,所以当x=1时,我们就直接让k%(y - 1),就ok了!这个就得到了后面的操作次数。 然后再加上余数就行了。

void solve() { 
	long long x, y, k;
	cin >> x >> y >> k;

	while (k > 0){
		long long need = min(k, y - x % y);
		if (k >= need){
			x += need;
			k -= need;
		}
		else{
			x += k;
			k = 0;
			break;
		}
		while (x % y == 0){
			x /= y;
		}
		if (x == 1){
			break;
		}
	}

	if (k){
		x = k % (y - 1) + 1;
	}

	cout << x << '\n';
}

C. Boring Day
题意:给定n个数(栈结构)和一个区间l,r。不断的从栈顶取出元素,如果连续取出的一些元素的和在l,r区间内,那么记数+1。
问最大计数是多少?每个元素只能参与一次计数,不能重复使用。

思路:维护一个双端队列,然后直接一次遍历即可。

void solve() { 
	int n, l, r;
	cin >> n >> l >> r;

	vector<int> a(n);
	for (int i = 0; i < n; ++i){
		cin >> a[i];
	}

	deque<int> dq;
	int ans = 0;
	long long sum = 0;
	for (int i = 0; i < n; ++i){
		dq.push_back(a[i]);
		sum += a[i];
		if (sum >= l && sum <= r){
			ans ++;
			dq.clear();
			sum = 0;
		}
		else{
			while(!dq.empty() && sum > r){
				sum -= dq.front();
				dq.pop_front();
			}
			if (l <= sum && sum <= r){
				ans ++;
				dq.clear();
				sum = 0;
			}
		}
	}

	cout << ans << '\n';
}

D. Beauty of the mountains

题意:给定nm的矩阵,矩阵中有两种东西用0和1表示,每个位置上有一个数值,然后给定一个kk的窗口,每次操作可以在这个窗口内对所有的数值±一个常数c(可以<0)。不限操作次数,问最后能否让矩阵种,0和1两种东西的总和相等。

思路:统计出0的sum和1的sum的差值diff,然后二维前缀和统计出每个位置0和1的数量,然后考虑每个k*k的窗口中0和1分别出现的次数,这个出现的次数代表了我可以纠正其中一方的sum的比例关系。求出所有窗口的出现元素次数后,我们考虑这些元素出现次数的线性组合,能否整除diff,如果能整除diff,那么有解,否则无解。

总结:赛时卡在了最后的线性组合上面,没有想到这个就是简单的贝组定理(拓展欧几里得),今天早上睡醒就知道应该考虑所有元素的gcd。所以还是输在数学太烂上面了。于是放弃玩游戏的时间,整理一下公约数这个东西。

辗转相除法的原理:假设g整除a和b,g是最大公约数,那么b % g一定等于0,a%g也一定等于0。那么(a - b)%g==0也一定成立。基于这个思想,我们不断的考虑(a % b)与b的关系,直到a % b == 0,此时的b就是gcd。

拓展欧几里得(贝组定理),就是D题的线性组合问题。如果在辗转相除的给过程中,不断的维护一个有a和b的表达式来表示余数r,那么最后当r=gcd的时候,这个表达式就是一个线性组合,可以让x * a + y * b = gcd。
简单描述一下这个过程,初始时1 * a + 0 * b = a, 0 * a + 1 * b = b,令x = y = 1, x1 = y1 = 0.
q = a / b
r = a - q * b = (x * a) + (y * b) - (x1 * a) - (y1 * b) =>a * (x - x1) + b * (y - y1)...,
然后a要=b,b要=r了,就令x = x1, y = y1, x1, y1=上面计算出来表示r的系数。

void solve() { 
    int n, m, k;
    cin >> n >> m >> k;

    vector<vector<int>> grid(n + 1, vector<int> (m + 1));
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= m; ++j){
            cin >> grid[i][j];
        }
    }

    vector<vector<char>> moun(n + 1, vector<char>(m + 1));
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= m; ++j){
            cin >> moun[i][j];
        }
    }

    long long a = 0;
    long long b = 0;
    vector<vector<int>> pref(n + 1, vector<int>(m + 1, 0));
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= m; ++j){
            pref[i][j] = (moun[i][j] == '0' ? -1 : 1);
            pref[i][j] += pref[i - 1][j];
            pref[i][j] += pref[i][j - 1];
            pref[i][j] -= pref[i - 1][j - 1];
            if (moun[i][j] == '0'){
                a += grid[i][j];
            }
            else{
                b += grid[i][j];
            }
        }
    }

    long long diff = (b - a);

    if (diff == 0){
        cout << "YES\n";
        return;
    }

    set<int> sett;
    for (int i = k; i <= n; ++i){
        for (int j = k; j <= m; ++j){
            int t = pref[i][j] - pref[i - k][j] - pref[i][j - k];
            t += pref[i - k][j - k];
            if (t) {
                if (diff % t == 0){
                    cout << "YES\n";
                    return;
                }
                sett.insert(abs(t));
            }
        }
    }

    int g = 0;
    for (const auto& cur : sett){
        g = ::gcd(g, cur);
        if (diff % g == 0){
            cout << "YES\n";   
            return;
        }
    }
    cout << "NO\n";
}

世界不会因为你的停歇而停止进步,但会因为你的参与而更加精彩(? 疯狂掉分,实在是感觉不到精彩在哪。。可能就是为了让别人更精彩:C)

有模板写题就是好用,只要维护固定的几个函数即可。

模板放在了下面的仓库,如果使用,请点进去点个star。
https://github.com/yxc-s/programming-template/tree/master
该仓库是一个新仓库,旨在打造一个通用的C++算法编程竞赛模板,包含数据结构,数论等各种实用的算法编程模板。如果您使用的语言不是C++,也可以将对应的代码实现翻译成其他语言来使用。

posted @   _Yxc  阅读(109)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示