10.24集训解题报告

T1 方程( \(equation\)

题面:

给定 \(4\) 个正整数 \(a\), \(b\), \(c\), \(d\),并且保证 \(c\) \(×\) \(d\) \(≤\) \(10^6\),请你求出有多少组正整数对 \((x, y)\) 满足如下方程:

\[\frac{a}{x}+\frac{b}{c}=\frac{d}{y} \]

本题共 \(20\) 个测试点,每个测试点 \(5\) 分。
• 对测试点 \(1\),保证 \(T = 0\)
• 对测试点 \(2\)\(16\),共 \(15\) 个测试点。\(a\), \(b\), \(c\), \(d\) 四个数中至少存在一个数为 \(1\)\(15\) 种情况,
每个测试点对应一种情况。
• 对测试点 \(17\)\(20\),没有特殊约定。
对全部的测试点,保证 \(0 ≤ T ≤ 20\)\(1 ≤ a, b, c, d ≤ 10^6\)\(c × d ≤ 10^6\)

思路:

化一下式子:
先通分:

\[acy+bxy=cdx \]

然后就:

\[cdx-bxy=acy \]

\[x(cd-by)=acy \]

\[x=\frac{acy}{cd-by} \]

因为 \(cd\) 不超过 \(10^6\),所以直接枚举 \(y\)

ll a, b, c, d; 
ll ans;
int T;
int main(){
	T=read();
	while(T--) {
		ans=0;
		a=read(); b=read(); c=read(); d=read();
		for(ll y=1; b*y<c*d; ++y) {
			if(a*c*y%(c*d-b*y)==0) ++ans;
		}
		cout<<ans; putchar('\n');
	}
}

T2 异或数列 ( \(xor\)

题面:

R 老师给了你一个数列 \(a_1, a_2, . . . a_n\)。数列中的数都是非负整数。
R 老师会进行 \(q\) 次操作,每次操作会给你两个数字 \(p\), \(x\),要求你把 \(a_p\) 和 $a_{p+1} 都对 \(x\) 做一次按位异或。每次操作完后,你必须告诉 R 老师,当前的数列里有多少个子区间 \([l, r]\) 满足 \(l ≤ r\) 且区间按位异或和为 \(0\)
• 对 \(10\%\) 的数据,保证 \(n, m ≤ 100\)
• 对 \(20\%\) 的数据,保证 \(n, m ≤ 300\)
• 对 \(40\%\) 的数据,保证 \(n, m ≤ 3000\)
• 对 \(70\%\) 的数据,保证 \(n, m ≤ 10^5\)\(a_i , x ≤ n\)
• 对 \(100\%\) 的数据,保证 $1 ≤ n, m ≤ 10^6,\(1 ≤ a_i, x ≤ 10^9,1 ≤ p ≤ n\)

思路:

非常套路的,先求一个异或前缀和,区间异或和为 \(0\) 当且仅当 \(r\)\(l-1\) 位置上的异或前缀和相等,初始的答案很好算,从左往右扫过去就行。
显然的,原数组就是前缀和数组的差分数组,所以题目让把 \(a_p\)\(a_{p+1}\) 都异或 \(x\),就是让 \(p\) 位置的前缀和数组异或上 \(x\)
那么他产生的贡献就是,先把原来的贡献减掉,然后这个点作为右端点把左边与这个位置新的值相同的个数加上,再以这个点为左端点把右边相同的个数加上。
总的看来,就是先把所有的异或前缀和的值存起来,算个数,然后改的时候,把所有和这个位置改之前一样的减去,再把所有和改之后一样的值加上。
\(unordered\)_\(map\) 减小常数。

代码:

int n, m, p, x;
int sz[1000006];
ll ans;
unordered_map<int, int>mp;
int main(){
	n=read(); m=read();
	mp[0]++;
	for(int i=1; i<=n; ++i) {
		sz[i]=read();
		sz[i]^=sz[i-1];
		ans+=mp[sz[i]]++;
	}
	while(m--) {
		p=read(); x=read();
		ans-=-1+mp[sz[p]]--;
		sz[p]^=x;
		ans+=mp[sz[p]]++;
		cout<<ans; putchar('\n');
	}
}

T3 制胡串问题 ( \(string\) )

题面:

\(S_i\) 表示字符串 \(S\) 的第 \(i\) 个字符,我们定义一个 \(01\)\(A\) 可以匹. 配. 一个只含字符 \(0\), \(1\), \(?\)
的字符串 \(B\) 当且仅当如下条件满足:

  1. \(A\)\(B\) 的长度相等。
  2. \(B_i = 0\),则 \(A_i = 0\)
  3. \(B_i = 1\),则 \(A_i = 1\)
  4. \(B_i = ?\),则 \(A_i\) 可以为 \(0\),也可以为 \(1\)
    现在,R 老师会有 \(q\) 次操作,操作共有两种:
  5. 给定 \([l, r]\),查询有多少字符串 \(S\),满足 S 能匹配 \([l, r]\) 内的所有字符串。
  6. 给定 \(x\) 和一个字符串 \(s\),将第 \(x\) 个字符串换成 \(s\)。保证 \(s\) 也是长度为 \(n\) 的只含 \(0, 1, ?\)的字符串。
测试点编号 m= q= n=
1∼3 102 102 10
4~6 1003 1003 10
7~9 1004 1004 30
10~12 100005 500005 1
13~14 100006 50006 30
15~20 100007 1000007 30

思路:

直接 \(30\) 棵线段树。

int n, m, q;
int bj[400100][31];
char c[100010][35];
char s[35];
int ans[31];
int Ans, re;
bool flag;
int sta[31], top;
inline void push_up(int g) {
	for(int i=0; i<n; ++i) {
		if(bj[g<<1][i]==3||bj[g<<1|1][i]==3) {bj[g][i]=3; continue ;}
		if(bj[g<<1][i]==bj[g<<1|1][i]) {bj[g][i]=bj[g<<1][i]; continue ;}
		if(bj[g<<1][i]==2||bj[g<<1|1][i]==2) bj[g][i]=min(bj[g<<1][i], bj[g<<1|1][i]);
		else bj[g][i]=3;
	}
}
inline void push_up2(int g) {
	for(int j=1; j<=top; ++j) {
		int i=sta[j];
		if(bj[g<<1][i]==3||bj[g<<1|1][i]==3) {bj[g][i]=3; continue ;}
		if(bj[g<<1][i]==bj[g<<1|1][i]) {bj[g][i]=bj[g<<1][i]; continue ;}
		if(bj[g<<1][i]==2||bj[g<<1|1][i]==2) bj[g][i]=min(bj[g<<1][i], bj[g<<1|1][i]);
		else bj[g][i]=3;
	}
}
inline void build(int g, int l, int r) {
	if(l==r) {
		scanf("%s", c[l]);
		for(int i=0; i<n; ++i) {
			if(c[l][i]=='0') bj[g][i]=0;
			if(c[l][i]=='1') bj[g][i]=1;
			if(c[l][i]=='?') bj[g][i]=2;
		}
		return ;
	}
	int mid=l+r>>1;
	build(g<<1, l, mid); build(g<<1|1, mid+1, r);
	push_up(g);
}
inline void Change(int g, int l, int r, int x) {
	if(l==r) {
		for(int j=1; j<=top; ++j) {
			int i=sta[j];
			if(c[l][i]=='0') bj[g][i]=0;
			if(c[l][i]=='1') bj[g][i]=1;
			if(c[l][i]=='?') bj[g][i]=2;
		}
		return ;
	} int mid=l+r>>1;
	if(x<=mid) Change(g<<1, l, mid, x); 
	else Change(g<<1|1, mid+1, r, x);
	push_up2(g);
}
inline void update(int g) {
	for(int i=0; i<n; ++i) {
		if(bj[g][i]==3||ans[i]==3) {ans[i]=3; flag=0; return;}
		if(bj[g][i]==ans[i]) continue ;
		if(bj[g][i]==2||ans[i]==2) ans[i]=min(ans[i], bj[g][i]);
		else {ans[i]=3; flag=0; return ;}
	}
}
inline void SUM(int g, int l, int r, int L, int R) {
	if(!flag) return ;
	if(L<=l&&r<=R) {
		update(g);
		return ;
	} int mid=l+r>>1;
	if(L<=mid) SUM(g<<1, l, mid, L, R);
	if(R>mid) SUM(g<<1|1, mid+1, r, L, R);
}
int main(){
	n=read(); m=read(); q=read();
	build(1, 1, m);
	while(q--) {
		int op=read();
		if(op) {
			int x=read(); scanf("%s", s);
			top=0;
			for(int i=0; i<n; ++i) if(c[x][i]!=s[i]) c[x][i]=s[i], sta[++top]=i;
			if(top)
			Change(1, 1, m, x);
		}
		else {
			int l=read(), r=read();
			for(int i=0; i<n; ++i) ans[i]=2;
			flag=1;
			SUM(1, 1, m, l, r);
			if(!flag) continue ;
			re=1;
			for(int i=0; i<n; ++i) {
				if(ans[i]==2) re*=2;
				if(ans[i]==3) {re=0; break;}
			}
			Ans^=re;
		}
	}
	cout<<Ans;
}

T4 游戏( \(game\)

题面:

这是一款一对一回合制游戏。双方玩家各有一个属性,被称为 \(delay\) 值,简称 \(d\) 值。\(d\) 值会根据回合中玩家使用的道具类型和数量来进行相应的增加。我们定义玩家 \(x\) 的回合为玩家 \(x\) 从发动攻击到结束的整个过程。在玩家 \(x\) 的回合中, 只有玩家 \(x\) 可以使用道具和发动攻击并且玩家 \(x\) 一定会发动攻击。当一个玩家的回合结束以后,下一回合将是两个玩家中 d 值较小的玩家的回合。当两个玩家的 \(d\) 值相同时,因为小 A 氪金很多,下一回合一定是小 A 的回合
这款游戏共有 \(m\) 种道具,第 \(i\) 种道具会将本回合的伤害增加不计算其他道具的原始伤害\(\frac{ki}{10^5}\) 倍,同时会增加 \(p_i\)\(d\) 值。每回合每种道具只能使用一次,本回合的道具不会对下回合产生伤害增益效果。同时,每回合结束以后,发动攻击的玩家一定会增加 \(w\)\(d\) 值。
而使用道具是受到双方 \(d\) 值差限制的。具体的,任何回合所使用的道具应该满足本回合结束以后双方 \(d\) 值(包括发动攻击的玩家一定增加的 \(w\)\(d\) 值)之差的绝对值不超过 100
一个显然的事实是,只要保证了 \(w ≤ 100\),玩家就一定存在一种选择道具的方法(包括不选择),来满足这个限制。
例如,在小 A 的回合中,若她的原始伤害 \(t = 10^5\),初始时 \(d\)\(d_0 = 3\),规定 \(w = 2\),她
使用了两个道具,\(k_1 = 114,k_2 = 514,p_1 = 19,p_2 = 81\),那么本回合她造成的伤害为

\[t + t × k_1 + t × k_2 = 105 + 114 + 514 = 100628 \]

她回合结束后的 \(d\) 值为

\[d0 + w + p_1 + p_2 = 3 + 2 + 19 + 81 = 105 \]

而假如下回合还是-她的回合,并且她没有使用道具,那么她下回合造成的伤害为

\[t = 100000 \]

她下回合结束后的 \(d\) 值为

\[10^5 + w = 10^5 + 2 = 107 \]

现在,小 A 和小 B 在这个游戏中进行单挑。给定游戏的道具列表,和他们的原始伤害、\(d\) 值。游戏一共会进行 \(n\) 回合,不妨认为无论造成多大的伤害,双方都不会死亡。请你最大化「小 A 对小 B 造成的伤害 - 小 B 对小 A 造成的伤害」这个差的值。
当然,小 B 也会尽可能最大化「她对小 A 造成伤害 - 小 A 对她造成伤害」的值。不妨认为双方都绝顶聪明,也就是他们都会选择最优的策略来使用道具而不会出错,题目所求即为在这种情况下伤害差的最大值。

测试点编号 n ≤ m ≤
1 0 10\(^5\)
1 0 10\(^5\)
2~3 10\(^3\) 0
4~6 5 5
7~10 3 10\(^5\)
11~14 10\(^3\) 10
15~20 10\(^3\) 10\(^5\)

对于全部的测试点,保证 \(0 ≤ n ≤ 10^3,0 ≤ m ≤ 10^5,1 ≤ p_i , w ≤ 100,1 ≤ x_a, x_b, k_a, d_a, d_b ≤10^9\)\(x_a\)\(x_b\)\(10^5\) 的倍数,\(1 ≤ |d_a − d_b| ≤ 100\)

思路:赛时没有思路。

代码:

if(n==0) return putchar('0'), 0;
if(m==0) {
	for(int i=1; i<=n; ++i) {
		if(da<=db) da+=w, ans1+=xa;
		else db+=w, ans2+=xb;
	}
	cout<<ans1-ans2;
	return 0;
}
posted @ 2022-10-24 23:00  Konnya_ku  阅读(34)  评论(0编辑  收藏  举报