Educational Codeforces Round 122 (Rated for Div. 2)

Educational Codeforces Round 122 (Rated for Div. 2)

Problem A Div. 7

\(t\)组询问,每组询问给出一个数\(x\),输出一个\(y\),满足\(y\)\(7\)​的倍数,最小化\(x\)\(y\)不同的数位差。

对于\(100\%\)​的数据:\(1 \le t \le 990, 10 \le x \le 999\)​。​

根据容斥原理,连续的\(7\)个数字中必然存在一个数是\(7\)的倍数,因此答案要么是\(1\),要么是\(0\)

\(x\)原本就是\(7\)的倍数的时候,答案为\(0\),这个时候不需要改动任何数。

\(x\)不是7的倍数的时候,答案为\(1\),只需要更改这个数的个位即可。

时间复杂度\(O(10t)\)

# include <bits/stdc++.h>
using namespace std;
int main() {
	int t; cin>>t;
	while (t--) {
		int x; cin>>x;
		if (x%7==0) {
			printf("%d\n",x);
			continue;
		}
		x/=10; x*=10;
		for (int i=0;i<=9;i++) if ((x+i)%7==0) {
			printf("%d\n",x+i); break;
		}
	}
	return 0;
}

Problem B Minority

给出\(t\)组询问,每个询问给出一个长度为\(n\)\(01\)字符串\(s\)​,最多可以进行一次这样的操作:

  • 选择一段连续字符串的区间\([l,r](1\le l \le r \le n)\),删除所有出现较少的字符\(0\)或者\(1\)

输出最大化后能删除的字符数。

对于\(100\%\)的数据满足:\(1\le t \le 10^4 , \sum n \le 2\times 10^5\)

如果\(0\)\(1\)的数目不一样多,那么最后的答案一定将是\(0\)\(1\)最少出现的字符全部删除。

首先,我们可以构造此情形,取\(l = 1,r = n\),则可以将\(0\)\(1\)最少出现的字符全部删除。

如果我们在字符串中任意选取一个区间\([l,r]\)​​,那么最少字符出现的次数必然不超过原字符串里出现最少字符的次数,可以用反证法来说明。

假如超过,那么这个字符必然是在原串中出现较多的字符,而在\([l,r]\)​​这个区间中,这个字符反而是出现次数最少的字符,这意味着另外一个字符是原串中出现较多的字符。前后矛盾,所以原命题成立。

如果\(0\)\(1\)的数目一样多,答案就是\(n/2-1\)​,我们可以忽略最后一个字符,按照上面的方法证明即可。

时间复杂度\(O(\sum n)\)​。

# include <bits/stdc++.h>
using namespace std;
int main() {
	int t; cin>>t;
	while (t--) {
		string s; cin>>s;
		int cnt[2]; cnt[0]=cnt[1]=0;
		for (int i=0;i<s.length();i++) cnt[s[i]-'0']++;
		if (cnt[0]!=cnt[1]) printf("%d\n",min(cnt[0],cnt[1]));
		else printf("%d\n",cnt[0]-1);
	}
	return 0;
}

Problem C Kill the Monster

给出\(t\)组询问,每组询问有\(h_c,d_c,h_m,d_m,k,w,a\)

\(h_c,d_c\)分别表示角色的生命值和攻击值;\(h_m,d_m\)表示怪兽的生命值和攻击值。

\(k\)表示角色能使用的魔法点,\(w,a\)分别表示使用\(1\)​魔法点可以提高角色的攻击值和生命值。

角色和怪兽轮番攻击,每次攻击让对方的生命下降攻击值的大小,当生命值不高于\(0\)​时判断死亡。

所有攻击开始前,角色可以使用不超过\(k\)点魔法能量武装自己,提高对应的生命值和攻击值。

对于每组询问,输出如果角色先攻击,角色是否能战胜怪兽,如果能输出YES否则输出NO

对于\(100\%\)​的数据满足:

\(1\le t \le 5\times 10^4 , 1 \le h_c,h_m \le 10^{15},1 \le d_c,d_m \le 10^{9}\)

$ 0 \le k \le 2\times 10^5,0\le w \le 10^4, 0 \le a \le 10^{10}$​

\(\sum k \le 2\times 10^5\)

最优的情况是用完所有\(k\)​点魔法能量,\(x(0\le x \le k)\)​点提高攻击值,\(k-x\)​​​点魔法能量提高生命值。

考虑每组询问用\(O(k)\)的时间枚举\(x\),那么问题转化为快速计算\(h_c,d_c,h_m,d_m\)条件下角色能否干掉怪兽。

可以计算出怪兽被角色击杀所需要的轮次\(k_m = \lceil \frac{h_m}{d_c} \rceil\),角色被怪兽击杀所需要的轮次\(k_c = \lceil \frac{h_c}{d_m} \rceil\)​。

由于角色是先手,那么角色能击杀怪兽的条件是:\(k_m \le k_c\),可以实现\(O(1)\)​判断。

注意要使用long long类型计算,不能使用double实数计算,可能存在偏差。

时间复杂度\(O(\sum k)\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
bool fun(int hc,int dc,int hm,int dm) {
	int kc = (hc % dm == 0ll) ? hc/dm : hc/dm+1ll;
	int km = (hm % dc == 0ll) ? hm/dc : hm/dc+1ll;
	return (km <= kc);
}
signed main() {
	int t; cin>>t;
	while (t--) {
		int hc,dc,hm,dm; cin>>hc>>dc>>hm>>dm;
		int k,w,a; cin>>k>>w>>a;
		bool flag = false;
		for (int i=0;i<=k;i++) {
			if (fun(hc+a*i,dc+w*(k-i),hm,dm)) {
				flag = true; break;
			}
		}
		puts(flag?"YES":"NO");
	}
	return 0;
}

Problem D Make Them Equal

给出\(t\)​组询问,每组询问给出\(n\)​和\(k\)​,及长度为\(n\)​的\(b_i,c_i\)​数组。

初始有一个全为\(1\)\(a\)数组,一次操作可以执行\(a_i += \lfloor \frac{a_i}{x} \rfloor\)

使用不超过\(k\)次操作,获得一个新的\(a_i\)数组,如果\(a_i = b_i\),那么可以获得\(c_i\)的分数。

最大化可以获得的分数。

对于\(100\%\)​​的数据满足:\(1 \le t \le 100,1 \le \sum n \le 10^3,1 \le b_i \le10^3 ,1 \le c_i \le 10^6, 0 \le k \le 10^6\)​​​。

首先我们可以用一个\(bfs\)​​,求出从\(w=1\)​​开始,执行\(w += \lfloor \frac{w}{x} \rfloor\)​​​,让\(w\)​​变成\(i\in[1,10^3]\)​​​中的任意一个数\(i\)​​最少需要几次操作\(d_i\)​。

实际计算表明,这个操作次数最多是\(12\)​​,所以这个\(bfs\)​​的时间复杂度大概为\(O(12n)\)​​。

然后问题转化为一个\(01\)​​背包的问题,第\(i\)​​个物品,价值为\(c_i\)​,体积为\(d[b_i]\)​。背包总容积为\(k\)​​。

注意到\(k\)有可能很大,但我们发现,如果\(k\)大到可以选取所有的物品,那么与\(k\)的实际大小就无关系。

这个极限值是\(0 \le \sum\limits_{i\in[1,n]} d[b_i] \le 12n\),因此实际的背包容易减小到\(\min\{k,\sum\limits_{i\in[1,n]} d[b_i]\}\)​。

时间复杂度为\(O(\sum n^2)\)

# include <bits/stdc++.h>
# define int long long
# define inf (1e9)
using namespace std;
const int N=1e3+10,M=12*N;
int f[N][M],b[N],c[N],d[N],step[N];
signed main() {
	int t; cin>>t;
	for (int i=1;i<=1000;i++) step[i]=inf;
	step[1]=0;
	queue<int>q; q.push(1);
	while (q.size()) {
		int u = q.front(); q.pop();
		for (int x=1;x<=u;x++) if (step[u+u/x]==inf) {
			step[u+u/x]=step[u]+1;
			q.push(u+u/x);
		}
	}
	while (t--) {
		int n,k; cin>>n>>k;
		for (int i=1;i<=n;i++) cin>>b[i];
		for (int i=1;i<=n;i++) cin>>c[i];
		for (int i=1;i<=n;i++) d[i]=step[b[i]];
		int tot = 0;
		for (int i=1;i<=n;i++) if (d[i]!=inf) tot+=d[i];
		k=min(k,tot);
		for (int i=0;i<=n;i++)
			for (int j=0;j<=k;j++)
				f[i][j] = 0;
		for (int i=1;i<=n;i++)
			for (int j=0;j<=k;j++) {
				f[i][j] = f[i-1][j];
				if (j-d[i]>=0) f[i][j]=max(f[i][j],f[i-1][j-d[i]]+c[i]);
			}
		int ans = 0;
		for (int i=0;i<=k;i++) ans=max(ans,f[n][i]);
		cout<<ans<<endl;	
	}
	return 0;
}

Problem E Spanning Tree Queries

给出一个\(n\)​个点,\(m\)​条边的无向图,和\(k\)​个询问。

每条边有一个权值\(w_i(1\le i \le m)\)

其中询问由参数\(q_i,a,b,c\)​​​​​生成,其中\(q_i\)​​​​​是长度为\(p\)​​​​的数组,表示前\(p\)​​​​个询问是\(q_i\)​​​。

对于\(j\in[p+1,k]\)的询问\(j\),满足\(q_j = (q_{j-1}\cdot a + b) \text{ mod } c\)​​。

对于每个询问\(q_i (1 \le i\le k)\)​,输出将每条边的权值暂时修改为\(|w_i - q_i|\)后的最小生成树边权和。

由于输出值过大,你只需要输出所有应该输出答案的异或和即可。

对于\(100\%\)的数据满足:

\(2\le n \le 50,n-1\le m\le 300,1\le p \le 10^5,p\le k\le10^7\)$

\(0 \le a,b,w \le 10^8 , 1\le c \le 10^8\)

首先,绝对值拆掉的条件有\(m\)​个,每个绝对值函数得到曲线都是一个V型曲线。

因此,任取两个绝对值曲线,会有\(1\)个交点,总共有\(m^2\)级别的交点。

在这些交点附近,最小生成树选取的边会发生改变,而在交点之间变化,最小生成树选取得到边不变。

如果再加上让绝对值内符号变化的\(m\)级别的点,那么这些点之间,最小生成树选取得到边不变,绝对值内符号也不变,只需要统计绝对值内符号取正和取负的个数,就可以快速计算结果。

因此,我们只需要再上述这些点上做一边最小生成树即可,而在点之间,只需要简单的处理就可以得到答案。

对询问排序,用双指针,可以快速计算每个询问的答案。

时间复杂度是\(O(m^3 \ log_2 m + k \ log_2 k)\)

# include <bits/stdc++.h>
# define ll long long
using namespace std;
const int N=55,M=305,K=1e7+10;
int n,m,size;
int f[N];
ll q[K];
struct Temp{int u,v,w,id;}t[M];
struct Edge{int u,v,w;}e[M];
struct Rec{ int x,cntp,cntn;ll ans;}r[M*M*10];
bool cmp2(Temp a,Temp b) {
	return a.w < b.w;
}
bool cmp3(Rec a,Rec b) {
	return a.x < b.x;
}
int father(int x) {
	if (f[x]==x) return x;
	return f[x]=father(f[x]);
}
void fun(int x) {
	for (int i=1;i<=m;i++) {
		t[i].u = e[i].u;
		t[i].v = e[i].v;
		t[i].w = abs(e[i].w - x);
		t[i].id = i;
	}
	sort(t+1,t+1+m,cmp2);
	for (int i=1;i<=n;i++) f[i]=i;
	int tot=0;
	ll res=0;
	int cntp=0,cntn=0;
	for (int i=1;i<=m;i++) {
		if (tot == n-1) break;
		int fu = father(t[i].u),fv = father(t[i].v);
		if (fu == fv) continue;
		f[fu] = fv;
		res=res+t[i].w; tot++;
		if ((x+1)-e[t[i].id].w>=0) cntp++;
		else cntn++;
	}
	++size;
	r[size].x=x;
	r[size].ans=res;
	r[size].cntp=cntp;
	r[size].cntn=cntn;
}
signed main() {
	cin>>n>>m;
	for (int i=1;i<=m;i++) {
		int u,v,w; cin>>u>>v>>w;
		e[i].u=u; e[i].v=v; e[i].w=w;
	}
	int p,k,a,b,c; cin>>p>>k>>a>>b>>c;
	for (int i=1;i<=p;i++) {
		cin>>q[i];
	}
	for (int i=p+1;i<=k;i++) {
		q[i]=(q[i-1]*a+b)%c;
	}
	sort(q+1,q+1+k);
	vector<int>tmp;
	for (int i=1;i<=m;i++)
		for (int j=i+1;j<=m;j++) {
			tmp.push_back(1ll*(e[i].w+e[j].w)/2);
			tmp.push_back(1ll*(e[i].w+e[j].w)/2-1);
			tmp.push_back(1ll*(e[i].w+e[j].w)/2+1);
		}
	for (int i=1;i<=m;i++) tmp.push_back(e[i].w);
	tmp.push_back(q[1]);
	sort(tmp.begin(),tmp.end());
	int T=unique(tmp.begin(),tmp.end())-tmp.begin();
	for (int i=0;i<T;i++) fun(tmp[i]);
	sort(r+1,r+1+size,cmp3);
	ll ans = 0;
	int cntp,cntn;
	int pt = 1,last;
	ll lastres;
	for (int i=1;i<=k;i++) {
		while (pt<=size && r[pt].x <= q[i]) {
			lastres = r[pt].ans;
			cntp = r[pt].cntp;
			cntn = r[pt].cntn;
			last = r[pt].x;
			pt++;
		}
		ans^=(1ll*(lastres+1ll*(cntp-cntn)*(q[i]-last)));
	}
	cout<<ans;
	return 0;
}
posted @ 2022-02-09 14:35  Maystern  阅读(107)  评论(0)    收藏  举报