题解:CF1312F Attack on Red Kingdom

最后更新于 2025-08-03 11:35:32
作者
分类 题解

Le probleme est simple a comprendre, mais difficile a programmer.

Chaque chateau est independant. On doit utiliser les fonctions SG pour calculer le resultat final.

Mais attention : Le nombre de troupes par chateau est tres grand. Mais la fonction SG se repete (cycle), Donc on trouve ce cycle pour calculer rapidement.

Solution :

Essayer tous les premiers coups possibles.

Verifier si le coup est legal avec la fonction SG.

Mais Comprendre $≠$ Savoir programmer !
J’ai donc ajoute beaucoup de commentaires dans le code.

Je ne suis pas tres bon en francais, mais mon editorial en anglais a ete rejete sans explication - juste des insulte. Donc, je pense que peut-etre je devrais l’ecrire en francais ?

Voici le code.

Desole mais les commentaires sont encore en anglais parce’que je ne suis pas assez bon en francais

/*
the essential constraint that allows us to
quickly find the loop knot is the fact
that x, y, z <= 5 so we will only
use the last 5 rows
*/
#include<bits/stdc++.h>
#define INF 1000000000000005
#define int long long
using namespace std;
int t;
int n, X, Y, Z, a[300005], cnt, sz, ans;
typedef vector<vector<int> > sta;
//this is a state of the sg function
//sg[i][j] meaning a castle with i
//troops remaining and the last attack
//being type j
sta looplog;
//keeping track of the values of
//all sg function <= the loop knot
int mex(vector<int> x){
	//umm do i need to explain how to find the mex
	//of a vector???
	for(int i = 0; i <= x.size(); i++){
		int check = 0;
		for(auto j: x)if(j == i)check = 1;
		if(check)continue;
		return i;
	}
	return 0;//yeah umm
}
void loop_knot(){
	//gotta find the loop knot first
	map<sta, int> d;
	d.clear();
	//wheter this combination of the
	//5 rows of vectors existed before
	//specifically each d keep track of
	//5 rows in the sg table each with
	//3 columes the 5 rows represent
	//the number of troops left from
	//i to i - 5 and columes are for the
	//type of the last move made
	sta cur(3, vector<int>(5, 0));
	//meaning cur is 3 vectors each with
	//5 0s in it
	//this is the initial vector just
	//like i said, cur[0, 1, 2] each
	//represent a colume with 5 elements in each
	//of them meaning the value of the sg
	//function for i, i - 1, i - 2 ... i - 5 troops
	//in a castle
	cnt = 0;
	//if there are cnt troops in a castle
	looplog.clear();
	while(!d.count(cur)){
		//while cur haven't existed
		//yet we continue computing later
		//curs, but if it has, we know
		//it MUST go into a cycle because
		//we ONLY use the last 5 rows
		//when transfering the sg function
		d[cur] = cnt;
		//cur first existed for the sg 
		//function of a castle with cnt troops
		looplog.push_back({cur[0].back(), cur[1].back(), cur[2].back()});
		//the bottom of each of the columes aka the
		//last row, is the value of the sg function if
		//u launch an attack of type 0 1 or 2 on a castle
		//with cnt troops remaining
		
		
		//compute the new cur value
		sta nw = cur;
		nw[0].push_back(mex({cur[0][5 - X], cur[1][5 - Y], cur[2][5 - Z]}));
		nw[1].push_back(mex({cur[0][5 - X], cur[2][5 - Z]}));
		nw[2].push_back(mex({cur[0][5 - X], cur[1][5 - Y]}));
		//for each colume, we add another number to it meaning
		//if we had 1 more soldier in the castle and we
		//last did move 0/1/2 on it
		//5 - X/Y/Z is because we're currently at colume
		//5 which means the sg function for cnt troops being in
		//the castle, so the sg function of cnt + 1 troops
		//is just cnt + 1 - X/Y/Z(depending on which move)
		//which is just 5 - X/Y/Z(depending on which move)
		//for cur, and taking mex is just cuz that's
		//the def of sg function
		
		nw[0].erase(nw[0].begin());
		nw[1].erase(nw[1].begin());
		nw[2].erase(nw[2].begin());
		//we only need to keep 5 rows because the 
		//earlier rows won't ever be used for transfering
		//so we erase the first row after each time we
		//increase a soldier
		
		cur = nw; 
		
		cnt++;
		//now we consider the case where there is 1 more
		//soldier in the castle
	}
	sz = cnt - d[cur];
	//sz = the size of each loop, because
	//we know we ended after cnt troops but
	//that's not the size of the loop knot
	//unless cur is also the first state to appear
	//but if it isn't the size of the loop knot is
	//just the second time cur appeared(which is cnt) - first
	//time which is just d[cur]
}
//alright good job u just walked through the most
//difficult part of this problem with me, now that
//we found the loop knots' size and kept track of all
//values <= loop knot in looplog we can O(1) caluclate
//the sg function value for any sg[x][y] meaning 
//a castle with x troops left and last attack being type y,
//by just doing the following = 
int sg_function(int x, int y){
//	cout<<x<<" "<<y<<endl;
	if(x < cnt){
		//from 0 to cnt is before
		//any value appeared more than once
		//they're also the spots that are recorded
		//DIRECTLY in log
		return looplog[x][y];
	}
	x -= (cnt - sz);
	//cnt - sz is the number of elements
	//that are not in the loop, the elements that
	//appeared before the first loop element appeared
	int tmp = (cnt - sz) + (x % sz);
	//x % sz = to which element in the loop have we
	//reached, + (cnt - sz) because we need to add
	//back the first unlooped elements
	return looplog[tmp][y];
	
}
signed main(){
	cin>>t;
	while(t--){
		cin>>n>>X>>Y>>Z;
		//x = mixed
		for(int i = 1; i <= n; i++){
			cin>>a[i];
		}
		loop_knot();
		//first we find the loop knot
		int orig = 0;
		for(int i = 1; i <= n; i++){
			orig ^= sg_function(a[i], 0);
			//if we don't do any attack
			//on any castle the value
			//is obviously sg[a[i]][0] bc
			//0 doesn't bring any restrictions
			//on the next attack and a[i]
			//means it's not yet attacked
		}
		ans = 0;
		for(int i = 1; i <= n; i++){
			orig ^= sg_function(a[i], 0);
			//if we don't not do an attack on
			//castle i instead what if 
			//we do a type 0/1/2 attack?
			ans += (orig == sg_function(max(0LL, a[i] - X), 0));
			ans += (orig == sg_function(max(0LL, a[i] - Y), 1));
			ans += (orig == sg_function(max(0LL, a[i] - Z), 2));
			//only when they're equal would they xor to 0
			//which is a must lose condition for the black
			//king who is about to make move next
			orig ^= sg_function(a[i], 0);
			//add it back
		}
		cout<<ans<<endl;
	}
	return 0;
}


S’il vous plait, si vous est le meme administrateur auparavant, n’insultez pas mon editorial, dites-moi simplement ce que je dois faire pour l’ameliorer s’il vous plait.