Codeforces Round 895 (Div. 3) A-F

div3 A-F

    • A. Two Vessels
    • B. The Corridor or There and Back Again
    • C. Non-coprime Split
    • D. Plus Minus Permutation
    • E. Data Structures Fan
    • F. Selling a Menagerie

A. Two Vessels

签到

#include

using namespace std;
const int N=1e6+5;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<ll,3> p3;
int mod=998244353;
const int maxv=4e6+5;

void solve()
{
	int a,b,c;
	cin>>a>>b>>c;
	int x=abs(a-b);
	int ans=(x+c*2-1)/(c*2);
	cout<<ans<<endl;

}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	while(t--){
		solve();
	}
    system("pause");
    return 0;
}

B. The Corridor or There and Back Again

思路:考虑对于每个炸弹,到达该位置后,在给定时间内能走过的最远距离。

#include

using namespace std;
const int N=1e6+5;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<ll,3> p3;
int mod=998244353;
const int maxv=4e6+5;



void solve()
{
	int n;
	cin>>n;
	vector<pll> a(n);
	for(int i=0;i<n;i++){
		int d,s;
		cin>>d>>s;
		a[i]={d,s};
	}
	sort(a.begin(),a.end());
	ll sum=2e9;
	for(int i=0;i<n;i++){
		auto [x,y]=a[i];
		sum=min(sum,x+(y-1)/2);
	}
	cout<<sum<<endl;
}







int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	while(t--){
		solve();
	}
    system("pause");
    return 0;
}

C. Non-coprime Split

思路:对于一个大于2的偶数 x x x,我们一定能将其分解为 x + ( x − 2 ) x+(x-2) x+x2的形式,所以大于二的偶数一定合法,那么考虑奇数的情况,对于奇数 y y y,如果其有一个不为本身且不为1的因子,那么我们一定能将其分解为 y + ( y − 2 ) y+(y-2) y+(y2)的形式。

综上所述:

对于一个区间 [ l , r ] [l,r] [l,r],如果 l − r > = 1 l-r>=1 lr>=1,并且合法的偶数不为2的情况下,那么我们一定可以选择这个区间中的某个偶数。

如果 l = = r l==r l==r,那么我们只需要对其进行质因数分解判断是否存在合法的质因子。

#include
using namespace std;
const int N=1e7+5;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<ll,3> p3;
int mod=998244353;
const int maxv=4e6+5;

void solve()
{
	int l,r;
	cin>>l>>r;
	if(l!=r){
		if(r%2!=0){
			r--;
		}
		if(r-2!=0) {
			cout<<2<<" "<<r-2<<endl;
			return ;
		}
		else {
			cout<<-1<<endl;
			return ;
		}
	}
	else{
		for(int i=2;i*i<=l;i++){
			if(l%i==0&&l/i>1){
				cout<<i<<" "<<l-i<<endl;
				return ;
			}
		}
	}
	cout<<-1<<endl;
}







int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	//ol();
	while(t--){
		solve();
	}
    system("pause");
    return 0;
}

D. Plus Minus Permutation

思路:因为n的数据范围很大,所以考虑这题是否跟结论挂钩。考虑所有 x x x位置和 y y y位置对答案的贡献,因为总贡献为 x x x的贡献减去 y y y的贡献,我们贪心的想,尽可能把大的数放在的位置,把小的数放在的位置,但对于 x x x y y y可能存在位置重合的情况,那么对于这种情况,该位置对答案的贡献为0。现在考虑如何求解 x x x y y y的贡献。

我们其实可以O(1)的算出 x x x位置的个数和 y y y位置的个数,同时我们也可以算出来重合位置的个数,因为所有重合位置的坐标即为x和y的最小公倍数,最后进行贡献计算即可。

#include

using namespace std;
const int N=1e7+5;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<ll,3> p3;
int mod=998244353;
const int maxv=4e6+5;

void solve()
{
	ll n,x,y;
	cin>>n>>x>>y;
	ll l=lcm(x,y);
	int cnt=n/l;
	ll c1=n/x;
	ll c2=n/y;
	c1-=cnt;
	c2-=cnt;
	ll sum=0;
	sum+=(n-c1+1+n)*c1/2;
	sum-=(1+c2)*c2/2;
	cout<<sum<<endl;
}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	ol();
	while(t--){
		solve();
	}
    system("pause");
    return 0;
}

E. Data Structures Fan

题意:

给你一个整数数组 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an,以及一个由 n n n个字符组成的二进制字符串由 n n n个字符组成。

给定 q q q个询问。询问有两种类型:

  • “1 l l l r r r”( 1 ≤ l ≤ r ≤ n 1\le l \le r \le n 1lrn)–用相反的字符替换 l ≤ i ≤ r l \le i \le r lir的每个字符 s i s_i si。即用 1 \texttt{1} 1替换所有 0 \texttt{0} 0,用 0 \texttt{0} 0替换所有 1 \texttt{1} 1

  • “2 g g g” ( g ∈ { 0 , 1 } g \in \{0, 1\} g{0,1}) - 计算所有索引 i i i 的数字 a i a_i ai 的 bitwise XOR 的值,使得 s i = g s_i = g si=g。注意,空数集的 XOR ⁡ \operatorname{XOR} XOR被认为等于 0 0 0
    给你一个整数数组 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an,以及一个由 n n n个字符组成的二进制字符串 3 3 3 n n n个字符组成。

例如,如果 n = 4 n = 4 n=4 a = [ 1 , 2 , 3 , 6 ] a = [1, 2, 3, 6] a=[1,2,3,6] s = 1001 s = \texttt{1001} s=1001,请考虑下面一系列的疑问:

  1. “2 0 0 0”–我们感兴趣的是 s i = 0 s_i = \tt{0} si=0的索引 i i i,因为 s = 1001 s = \tt{1001} s=1001,这些索引是索引 2 2 2 3 3 3,所以查询的答案将是 a 2 ⊕ a 3 = 2 ⊕ 3 = 1 a_2 \oplus a_3 = 2 \oplus 3 = 1 a2a3=23=1
  2. “1 1 1 1 3 3 3”–我们需要将字符 s 1 , s 2 , s 3 s_1, s_2, s_3 s1,s2,s3替换为它们的对立面,因此查询前为 s = 1001 s = \tt{1001} s=1001,查询后为 s = 0111 s = \tt{0111} s=0111 s = 0111 s = \tt{0111} s=0111.
  3. “2 1 1 1”–我们感兴趣的是 s i = 1 s_i = \tt{1} si=1的索引 i i i,因为 s = 0111 s = \tt{0111} s=0111,这些索引就是 2 2 2 3 3 3 4 4 4,所以查询的答案将是 a 2 ⊕ a 3 ⊕ a 4 = 2 ⊕ 3 ⊕ 6 = 7 a_2 \oplus a_3 \oplus a_4 = 2 \oplus 3 \oplus 6 = 7 a2a3a4=236=7
  4. “1 2 2 2 4 4 4” - s = 0111 s = \tt{0111} s=0111 → \to s = 0000 s = \tt{0000} s=0000.
  5. “2 1 1 1” - s = 0000 s = \tt{0000} s=0000,没有带 s i = 1 s_i = \tt{1} si=1的索引,所以因为空数集的 XOR ⁡ \operatorname{XOR} XOR被认为等于 0 0 0,所以这个查询的答案是 0 0 0

† ^{\dagger} 二进制字符串是指只包含字符 0 \texttt{0} 0 1 \texttt{1} 1的字符串。

思路:
法一:运用异或相关的性质。
比如给定数组 a 1 , a 2 , a 3 , a 4 , a 5 a_1,a_2,a_3,a_4,a_5 a1,a2,a3,a4,a5,给定字符串为 01110 01110 01110,我们可以先计算出 s i s_i si等于1的值,为 a 2 ⊕ a 3 ⊕ a 4 a_2 \oplus a_3 \oplus a_4 a2a3a4,同样,我们可以计算出 s i s_i si等于0的值,为 a 1 ⊕ a 5 a_1 \oplus a_5 a1a5
考虑修改操作,我们可以先预处理出来数组的异或前缀和,还是以上文为例,为: a 1 ⊕ a 2 ⊕ a 3 ⊕ a 4 ⊕ a 5 a_1\oplus a_2 \oplus a_3 \oplus a_4 \oplus a_5 a1a2a3a4a5,我们如果对区间 [ 4 , 5 ] [4,5] [4,5]进行修改,修改后字符串变为 01101 01101 01101,值为1的结果为 a 2 ⊕ a 3 ⊕ a 5 a_2 \oplus a_3 \oplus a_5 a2a3a5,我们会发现,我们原来1的结果为 a 2 ⊕ a 3 ⊕ a 4 a_2 \oplus a_3 \oplus a_4 a2a3a4,我们和区间 [ 4 , 5 ] [4,5] [4,5]的异或值进行异或,为 a 2 ⊕ a 3 ⊕ a 4 ⊕ a 4 ⊕ a 5 a_2 \oplus a_3 \oplus a_4\oplus a_4\oplus a_5 a2a3a4a4a5,即为 a 2 ⊕ a 3 ⊕ a 5 a_2 \oplus a_3 \oplus a_5 a2a3a5;对于0的情况也同理。

#include

using namespace std;
const int N=1e7+5;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<ll,3> p3;
int mod=998244353;
const int maxv=4e6+5;


void solve()
{
	int n;
	cin>>n;
	vector<ll> a(n+5);
	vector<ll> s(n+5);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		s[i]=s[i-1]^a[i];
	}
	string st;
	cin>>st;
	st=" "+st;
	ll tot=s[n];
	ll tot1=0;
	ll tot2=0;
	for(int i=1;i<=n;i++){
		if(st[i]=='1') tot1^=a[i];
		else tot2^=a[i];
	}
	int q;
	cin>>q;
	while(q--){
		int op;
		cin>>op;
		if(op==1){
			int l,r;
			cin>>l>>r;
			ll val=s[l-1]^s[r];
			tot1^=val;
			tot2^=val;
		}
		else{
			int g;
			cin>>g;
			if(g==1){
				cout<<tot1<<" ";
			}
			else{
				cout<<tot2<<" ";
			}
		}
	}
	cout<<endl;
}
int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	//ol();
	while(t--){
		solve();
	}
    system("pause");
    return 0;
}

法二:线段树板子
思路:我们使用线段树,对于线段树的每个节点,我们储存两个值,分别为节点所在区间的在二进制字符串中为0的异或值的和与1的异或值的和。然后进行正常的区间修改和查询即可。具体思路看代码注释

#include
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<ll,3> p3;
int mod=998244353;
const int maxv=4e6+5;

struct node
{
	int l,r,b[2],tag;
	#define l(x) tr[x].l
	#define r(x) tr[x].r
	#define tag(x) tr[x].tag
	#define b0(x) tr[x].b[0]
	#define b1(x) tr[x].b[1]
} tr[N*4];
string s;
int a[N];

void update(int p)
{
	b0(p)=b0(p*2)^b0(p*2+1);
	b1(p)=b1(p*2)^b1(p*2+1);
}

void bulid(int p,int l,int r)
{
	if(l==r){
		l(p)=l,r(p)=r;
		if(s[l]=='1'){
			tr[p].b[1]=a[l];
			tr[p].b[0]=0;
		} 
		else {
			tr[p].b[0]=a[l];
			tr[p].b[1]=0;
		}
		tag(p)=0;
		return ;
	}
	l(p)=l,r(p)=r,b0(p)=b1(p)=tag(p)=0;
	int mid=(l+r)/2;
	bulid(p*2,l,mid);
	bulid(p*2+1,mid+1,r);
	update(p);
}

void pushdown(int p)//pushdown和modify中同理,把子节点进行交换
{
	if(tag(p)){
		swap(b0(p*2),b1(p*2));
		swap(b0(p*2+1),b1(p*2+1));
		tag(p*2)^=1;
		tag(p*2+1)^=1;
	}
	tag(p)=0;
}

void modify(int p,int l,int r)
{
	if(l<=l(p)&&r(p)<=r){
		swap(b0(p),b1(p));//修改把区间中储存的0和1进行交换即可
		tag(p)^=1;
		return ;
	}
	pushdown(p);
	int mid=(l(p)+r(p))/2;
	if(l<=mid) modify(p*2,l,r);
	if(r>mid) modify(p*2+1,l,r);
	update(p);
}

int query(int p,int l,int r,int f)//query函数其实不用写,因为这题的查询相当于查询区间[1,n]
{
	if(l<=l(p)&&r(p)<=r){
		if(f==1) return b1(p);
		else return b0(p);
	}
	pushdown(p);
	int mid=(l(p)+r(p))/2;
	int res=0;
	if(l<=mid) res^=query(p*2,l,r,f);
	if(r>mid) res^=query(p*2+1,l,r,f);
	return res;
}


void solve()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	cin>>s;
	s=" "+s;
	bulid(1,1,n);
	int q;
	cin>>q;
	while(q--){
		int op;
		cin>>op;
		if(op==1){
			int l,r;
			cin>>l>>r;
			modify(1,l,r);
		}
		else{
			int g;
			cin>>g;
			cout<<query(1,1,n,g)<<" ";
		}
	}
	cout<<endl;
}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	//ol();
	while(t--){
		solve();
	}
    system("pause");
    return 0;
}

F. Selling a Menagerie

题意:您是一个动物园的主人,动物园里有 n n n只动物,编号从 1 1 1 n n n。然而,维持动物园的费用相当昂贵,因此您决定出售动物园!

众所周知,每只动物都害怕另一只动物。更确切地说,动物 i i i害怕动物 a i a_i ai a i ≠ i a_i \neq i ai=i)。另外,每只动物的成本是已知的,对于动物 i i i来说,成本等于 c i c_i ci

您将按固定顺序出售所有动物。从形式上看,您需要选择某种排列【1011】,先卖出动物 p 1 p_1 p1,然后卖出动物 p 2 p_2 p2,以此类推,最后卖出动物 p n p_n pn

当你卖出动物 i i i时,有两种可能的结果:

  • 如果动物 a i a_i ai是在动物 i i i之前***卖出的,那么你卖出动物 i i i会得到 c i c_i ci元钱。
  • 如果动物 a i a_i ai在动物 i i i之前***没有卖出,那么卖出动物 i i i可以得到 2 ⋅ c i 2 \cdot c_i 2ci金钱。 令人惊讶的是,目前害怕的动物更值钱)。

你的任务是选择出售动物的顺序,使总利润最大化。

例如,如果 a = [ 3 , 4 , 4 , 1 , 3 ] a = [3, 4, 4, 1, 3] a=[3,4,4,1,3] c = [ 3 , 4 , 5 , 6 , 7 ] c = [3, 4, 5, 6, 7] c=[3,4,5,6,7],而你选择的排列顺序是 [ 4 , 2 , 5 , 1 , 3 ] [4, 2, 5, 1, 3] [4,2,5,1,3],那么:

  • 第一只出售的动物是动物 4 4 4。动物 a 4 = 1 a_4 = 1 a4=1以前没有卖过,所以卖掉它你可以得到 2 ⋅ c 4 = 12 2 \cdot c_4 = 12 2c4=12元钱。
  • 第二只要出售的动物是动物 2 2 2。动物 a 2 = 4 a_2 = 4 a2=4之前已经卖出,所以卖出它你会得到 c 2 = 4 c_2 = 4 c2=4金钱。
  • 第三只要出售的动物是动物 5 5 5。动物 a 5 = 3 a_5 = 3 a5=3之前没有卖出过,所以卖出它你会得到 2 ⋅ c 5 = 14 2 \cdot c_5 = 14 2c5=14金钱。
  • 第四只要出售的动物是动物 1 1 1。动物 a 1 = 3 a_1 = 3 a1=3之前没有卖出过,所以卖出它你会得到 2 ⋅ c 1 = 6 2 \cdot c_1 = 6 2c1=6金钱。
  • 第五只要出售的动物是动物 3 3 3。动物 a 3 = 4 a_3 = 4 a3=4之前被卖掉了,所以卖掉它你可以得到 c 3 = 5 c_3 = 5 c3=5金钱。

在这种排列方式下,你的总利润为 12 + 4 + 14 + 6 + 5 = 41 12 + 4 + 14 + 6 + 5 = 41 12+4+14+6+5=41。请注意,在这个例子中, 41 41 41并不是***可能的最大利润。

† ^\dagger 长度为 n n n的排列是一个数组,由 n n n个不同的整数组成,这些整数从 1 1 1 n n n依次排列。例如, [ 2 , 3 , 1 , 5 , 4 ] [2,3,1,5,4] [2,3,1,5,4]是一个排列,但 [ 1 , 2 , 2 ] [1,2,2] [1,2,2]不是排列( 2 2 2在数组中出现两次), [ 1 , 3 , 4 ] [1,3,4] [1,3,4]也不是排列( n = 3 n=3 n=3,但 4 4 4在数组中出现)。

思路:拓扑排序。
动物 i i i害怕动物 a i a_i ai,并且对于第 i i i个动物,动物 a i a_i ai没有在它前面卖出,那么我们的收获会越大,所以我们贪心的想,让对于每一个 i i i,我们贪心的认为,让所有令 i i i害怕的动物全部在 i i i后进行售卖。对 i i i a i a_i ai进行连边,那么就转为了求在该图下的拓扑序列,但又因为存在环,所以我们需要断环为链,我们同样贪心的想,我们想让断环为链的损失最小,所以我们找到价值最小的那个动物x,然后让令x害怕的那个动物排在第一位即可,因为这样整个环只有价值最小的那个动物以价格 c i c_i ci卖出。具体注释看代码。

#include

using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef array<int,4> p4;
int mod=998244353;
const int maxv=4e6+5;

vector<int> e[N];
int d[N],st[N];//st数组用于标记已经在拓扑序中的点
int n;
vector<int> a(N),c(N);
vector<int> ans;
int val=2e9,idx=0;
void add(int u,int v)
{
	e[u].push_back(v);
}

void dfs(int x)
{
	st[x]=1;
	for(auto u: e[x]){
		if(c[u]<val){
			val=c[u],idx=u;
		}
		if(!st[u]){
			dfs(u);
		}
		else return;
	}
}

void topsort()//拓扑排序板子
{
	queue<int >q;
	for(int i=1;i<=n;i++) {
		if(d[i]==0) q.push(i);
	}
	while(!q.empty()){
		auto t=q.front();
		q.pop();
		st[t]=1;
		ans.push_back(t);
		for(auto x: e[t]){
			d[x]--;
			if(d[x]==0) q.push(x);
		}
	}
}

void solve()
{
	cin>>n;
	memset(st,0,(n+5)*sizeof (int));
	memset(d,0,(n+5)*sizeof (int));
	for(int i=1;i<=n;i++) {
		e[i].clear();
	}
	ans.clear();
	for(int i=1;i<=n;i++ ){
		cin>>a[i];
		add(i,a[i]);
		d[a[i]]++;
	}
	for(int i=1;i<=n;i++) cin>>c[i];
	topsort();//拓扑序进行完毕,再考虑断环为
	for(int i=1;i<=n;i++){//dfs找环上价值最小的点
		if(!st[i]){
			val=2e9,idx=0;
			dfs(i);
			int x=a[idx];//最先放入x的后继
			do{
				ans.push_back(x);
				x=a[x];
			}while(x!=a[idx]);
		}
	}
	for(auto x: ans) cout<<x<<" ";
	cout<<endl;
}


int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	while(t--){
		solve();
	}
	system("pause");
	return 0;
}

你可能感兴趣的:(cf补题,算法,c++,数据结构)