Codeforces Round 988 (Div. 3)题解记录(A~G)

比赛链接:https://codeforces.com/contest/2037
本场F题卡了快一小时?G题20分钟内一直在想如何容斥,没想出来。

A. Twice

题面:

Kinich 迎来了新的一天。他打开手机,检查邮箱,发现了一个神秘的礼物。他决定打开这个礼物。

Kinich 打开了一个包含 \(n\) 个整数的数组 \(a\)。起初,Kinich 的得分是 \(0\)。他将执行以下操作任意次数:

选择两个索引 \(i\)\(j\) \((1 \leq i < j \leq n)\),使得在任何之前的操作中都没有选择过 \(i\)\(j\),并且 \(a_i = a_j\)。然后,将他的得分增加 \(1\)
输出在执行上述操作任意次数后,Kinich 能够达到的最大得分。

为了计算Kinich能达到的最大得分,我们需要统计数组中每个数字出现的次数,然后对于出现次数超过一次的数字,将它们的出现次数除以2后求和。这是因为每对相同的数字可以为Kinich增加一分。
输入:
第一行包含一个整数 \(t\)\(1 \leq t \leq 500\))——测试用例的数量。

每个测试用例的第一行包含一个整数 \(n\)\(1 \leq n \leq 20\))——数组 \(a\) 的长度。

每个测试用例的下一行包含 \(n\) 个用空格分隔的整数 \(a_1, a_2, \ldots, a_n\)\(1 \leq a_i \leq n\)
输出:
对于每个测试用例,输出最大分数。
样例:
5
1
1
2
2 2
2
1 2
4
1 2 3 1
6
1 2 3 1 2 3
——————
0
1
0
1
3
思路:显然非常签,可以统计不同数字出现次数,统计完后,每个不同数个数除二的值全部加起来即可

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                     long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
	ll ans = 1;
	while (y)
	{
		if (y & 1)
		{
			ans = ans % mod * (x % mod) % mod;
		}
		x = x % mod * (x % mod) % mod;
		y >>= 1;
	}
	return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
	if (y == 0)
		return x;
	else
		return gcd(y, x % y);
}
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
ll a[250000];
int main()
{
	fio();
	ll t;
	cin>>t;
	while(t--)
	{
		ll n;
		cin>>n;
		map<ll,ll>q;
		ll cnt=0;
		for(ll i=1;i<=n;i++)
		{
			ll x;
			cin>>x;
			if(q[x]==0)q[x]++;
			else cnt++,q[x]=0;
		}
		cout<<cnt<<endl;
	}

}

B. Intercepted Inputs

题面:
第一行包含两个整数 \(n\)\(m\) —— 网格的尺寸。

接下来的 \(n\) 行每行包含 \(m\) 个整数 —— 网格的数值。

然而,有人截获了你的输入流,将所有给定的整数打乱,并把它们全部放在一行中!现在有 \(k\) 个整数全部放在一行中,你不知道每个整数原来属于哪里。与其让 Citlali 重新发送输入,你决定自己确定 Citlali 可能提供的 \(n\)\(m\) 的值。
输入:
第一行包含一个整数 \(t\)\(1 \leq t \leq 10^4\))——测试用例的数量。

每个测试用例的第一行包含一个整数 \(k\)\(3 \leq k \leq 2 \cdot 10^5\))——输入流中的总输入数。

每个测试用例的下一行包含 \(k\) 个整数 \(a_1, a_2, \ldots, a_k\)\(1 \leq a_i \leq k\))——你的输入流中被打乱的输入。保证 \(n\)\(m\) 包含在 \(k\) 个整数中。

保证所有测试用例中 \(k\) 的总和不超过 \(2 \cdot 10^5\)
输出:
对于每个测试用例,输出两个整数,一个可能的 \(n\)\(m\) 的值。如果存在多个可能的答案,输出任意一个。
样例:
5
3
1 1 2
11
3 3 4 5 6 7 8 9 9 10 11
8
8 4 8 3 8 2 8 1
6
2 1 4 5 3 3
8
1 2 6 3 8 5 5 3
——————
1 1
3 3
2 3
4 1
1 6
思路:输入中,一定会有行和列,其他一定为行列中的数,所有看是否有两个数相乘等于\(n-2\),用map记录下现有的,然后看看是否能整除n-2,如果能就用map查另一个因数是否存在,否则继续

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                     long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
	ll ans = 1;
	while (y)
	{
		if (y & 1)
		{
			ans = ans % mod * (x % mod) % mod;
		}
		x = x % mod * (x % mod) % mod;
		y >>= 1;
	}
	return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
	if (y == 0)
		return x;
	else
		return gcd(y, x % y);
}
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
ll a[250000];
int main()
{
	fio();
	ll t;
	cin>>t;
	while(t--)
	{
		map<ll,ll>q;
		ll n;
		cin>>n;
		ll u=n-2;
		ll l,r;
		for(ll i=1;i<=n;i++)
		{
			ll x;
			cin>>x;
			if(x*x==u)
			{
				q[x]++;
				if(q[x]>=2)
				{
					l=x,r=x;
				}
			}
			else if(u%x==0)
			{
				q[x]++;
				if(q[x]&&q[u/x])
				{
					l=x,r=u/x;
				}
			}
		}
		cout<<l<<" "<<r<<endl;
	}

}

C. Superultra's Favorite Permutation

题面:
Superultra,一只小红熊猫,非常想要primogems。在他的梦中,一个声音告诉他,他必须解决以下任务才能获得终身供应的primogems。帮助Superultra!

构造一个长度为 \(n\) 的排列 \(p\),使得对于所有的 \(1 \leq i \leq n - 1\)\(p_i + p_{i+1}\) 是合数。如果不可能,输出 \(-1\)

\(^{\text{∗}}\) 长度为 \(n\) 的排列是一个由 \(n\) 个不同的整数从 \(1\)\(n\) 组成的数组,顺序任意。例如,\([2,3,1,5,4]\) 是一个排列,但 \([1,2,2]\) 不是排列(\(2\) 在数组中出现了两次),并且 \([1,3,4]\) 也不是排列(\(n=3\) 但是数组中有 \(4\))。

\(^{\text{†}}\) 如果一个整数 \(x\) 除了 \(1\)\(x\) 之外至少还有一个因数,那么 \(x\) 是合数。例如,\(4\) 是合数,因为 \(2\) 是一个因数。
输入:
第一行包含 \(t\)\(1 \leq t \leq 10^4\))——测试用例的数量。

每个测试用例包含一个整数 \(n\)\(2 \leq n \leq 2 \cdot 10^5\))——排列的长度。

保证所有测试用例中 \(n\) 的总和不超过 \(2 \cdot 10^5\)
输出:
对于每个测试用例,如果无法构造排列 \(p\),则在新的一行输出 \(-1\)。否则,在新的一行输出 \(n\) 个整数 \(p_1, p_2, \ldots, p_n\)
样例:
2
3
8
——————
-1
1 8 7 3 6 2 4 5
思路:构造题得自己动手写写,枚举到5发现2,4,5,1,3可以,然后想了下,偶数+偶数是合数,两个非1奇数相加是合数,于是左边,放所有剩余偶数,右边放所有剩余奇数就行了

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                     long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
	ll ans = 1;
	while (y)
	{
		if (y & 1)
		{
			ans = ans % mod * (x % mod) % mod;
		}
		x = x % mod * (x % mod) % mod;
		y >>= 1;
	}
	return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
	if (y == 0)
		return x;
	else
		return gcd(y, x % y);
}
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
ll a[250000];
ll b[250000];
int main()
{
	fio();
	ll t;
	cin>>t;
	while(t--)
	{
		ll n;
		cin>>n;
		if(n<=4)
		cout<<-1<<endl;
		else 
		{
			ll l,r;
			l=r=0;
			for(ll i=6;i<=n;i++)
			{
				if(i%2==0)
				{
					l++;
					a[l]=i;
				}
				else 
				{
					r++;
					b[r]=i;
				}
			}
			for(ll i=1;i<=l;i++)cout<<a[i]<<" ";
			cout<<2<<" "<<4<<" "<<5<<" "<<1<<" "<<3<<" ";
			for(ll i=1;i<=r;i++)cout<<b[i]<<" ";
			cout<<endl;
		}
	}

}

D. Sharky Surfing

题面:
第一行包含两个整数 \(n\)\(k\),用空格分隔 —— 分别表示障碍的数量和 Mualani 最初的跳跃能力。

第二行包含 \(n\) 个区间 \([l, r]\) 的开始位置,用空格分隔 —— 表示障碍的开始位置。

第三行包含 \(n\) 个整数 \(r\),用空格分隔 —— 表示障碍的结束位置。

第四行包含 \(m\) 个整数 \(x_i\),用空格分隔 —— 表示能量提升的位置。

对于每个能量提升位置 \(x_i\),接下来的一行包含一个整数 \(v_i\),表示能量提升的价值。

如果无法完成冲浪路径,输出 \(-1\)
输入:
第一行包含一个整数 \(t\)\(1 \leq t \leq 10^4\))——测试用例的数量。

每个测试用例的第一行包含三个整数 \(n\)\(m\)\(L\)\(1 \leq n, m \leq 2 \times 10^5\)\(3 \leq L \leq 10^9\))——障碍的数量、能力提升道具的数量和终点的位置。

接下来的 \(n\) 行每行包含两个整数 \(l_i\)\(r_i\)\(2 \leq l_i \leq r_i \leq L-1\))——第 \(i\) 个障碍的区间界限。保证对于所有 \(1 \leq i < n\),有 \(r_i + 1 < l_{i+1}\)(即所有障碍都是不重叠的,按位置递增排序,并且前一个障碍的终点与下一个障碍的起点不连续)。

接下来的 \(m\) 行每行包含两个整数 \(x_i\)\(v_i\)\(1 \leq x_i, v_i \leq L\))——第 \(i\) 个能力提升道具的位置和价值。保证对于所有 \(1 \leq i < m\),有 \(x_i \leq x_{i+1}\)(即能力提升道具按位置非递减排序),并且没有任何能力提升道具在任何障碍的区间内。

保证所有测试用例中 \(n\)\(m\) 的总和不超过 \(2 \times 10^5\)
输出:
对于每个测试用例,输出她必须收集的最小道具数量才能到达位置 L
如果无法到达,则输出 −1
样例:
4
2 5 50
7 14
30 40
2 2
3 1
3 5
18 2
22 32
4 3 50
4 6
15 18
20 26
34 38
1 2
8 2
10 2
1 4 17
10 14
1 6
1 2
1 2
16 9
1 2 10
5 9
2 3
2 2
——————
4
-1
1
2

思路:在一个障碍之前的所有道具,用优先队列先存起来,如果这个障碍跳不过去,就使用优先队列存起来的,如果用空了还过不去就输出-1,对于下一个障碍重复此操作即可,对于道具位置存储和障碍位置存储这里开了数组,然后用了不同指针指向他们现在的位置。

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                     long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
	ll ans = 1;
	while (y)
	{
		if (y & 1)
		{
			ans = ans % mod * (x % mod) % mod;
		}
		x = x % mod * (x % mod) % mod;
		y >>= 1;
	}
	return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
	if (y == 0)
		return x;
	else
		return gcd(y, x % y);
}
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
ll a[250000];
ll b[250000];
struct s
{
	ll x, y;
}p[250000];
s p1[250000];
int main()
{
	fio();
	ll t;
	cin>>t;
	while(t--)
	{
		ll n,m,l;
		cin>>n>>m>>l;
		for(ll i=1;i<=n;i++)
		{
			cin>>p[i].x>>p[i].y;
		}
		for(ll i=1;i<=m;i++)
		{
			cin>>p1[i].x>>p1[i].y;
		}
		ll cnt=1;
		ll o=1;
		ll wz=1;
		ll c=1;
		priority_queue<ll>f;
		ll pd=0;
		ll gs=0;
		while(wz<p[o].x&&o<=n)
		{
			while(c<=m)
			{
				if(p1[c].x<p[o].x)
				{
					f.push(p1[c].y);
					c++;
				}
				else break;
			}
			while(!f.empty()&&cnt+p[o].x-1<=p[o].y)
			{
				gs++;
				cnt+=f.top();
				f.pop();
			}
			if(cnt+p[o].x-1<=p[o].y)
			{
				pd=1;
				break;
			}
			else 
			{
				wz=p[o].y+1;
				o++;
			}
		}
		if(pd||wz!=p[n].y+1)
		cout<<-1<<endl;
		else 
		cout<<gs<<endl;
	}
}

E. Kachina's Favorite Binary String

题面:
Kachina 挑战你猜出她最喜欢的长度为 \(n\) 的二进制字符串 \(s\)。她定义了 \(f(l, r)\) 为在 \(s_l s_{l+1} \ldots s_r\) 中的 \(\texttt{01}\) 子序列的数量。如果两个子序列是通过从原始字符串中删除不同位置的字符形成的,即使得到的子序列包含相同的字符,它们也被认为是不同的。

为了确定 \(s\),你可以问她一些问题。在每个问题中,你可以选择两个索引 \(l\)\(r\)\(1 \leq l < r \leq n\)),并询问她 \(f(l, r)\) 的值。

在询问 Kachina 不超过 \(n\) 个问题后,确定并输出 \(s\)。然而,可能存在无法确定 \(s\) 的情况。在这种情况下,你需要报告 \(\texttt{IMPOSSIBLE}\)

形式上,如果询问 \(n\) 个问题后,对于 \(s\) 总是存在多种可能的字符串,无论提出什么问题,那么 \(s\) 就无法被确定。请注意,如果你报告 \(\texttt{IMPOSSIBLE}\) ,当存在一系列最多 \(n\) 个查询可以唯一确定二进制字符串时,你将得到错误答案。

\(^{\text{∗}}\) 二进制字符串只包含字符 \(\texttt{0}\)\(\texttt{1}\)

\(^{\text{†}}\) 如果序列 \(a\) 可以通过从序列 \(b\) 中删除若干个(可能是零个或全部)元素来获得,那么 \(a\) 就是 \(b\) 的一个子序列。例如,\(\mathtt{1011101}\) 的子序列有 \(\mathtt{0}\)\(\mathtt{1}\)\(\mathtt{11111}\)\(\mathtt{0111}\),但不包括 \(\mathtt{000}\)\(\mathtt{11100}\)
输入:
第一行输入包含一个整数 \(t\)\(1 \leq t \leq 10^3\))——测试用例的数量。

每个测试用例的第一行包含一个整数 \(n\)\(2 \leq n \leq 10^4\))——\(s\) 的长度。

保证所有测试用例中 \(n\) 的总和不超过 \(10^4\)
交互说明翻译:
要提出一个问题,请按照以下格式输出一行(不要包含引号):

"\(\texttt{? l r}\)"(\(1 \leq l < r \leq n\)
裁判将返回一个整数 \(f(l, r)\)

当你准备打印答案时,请按照以下格式输出一行:

如果 \(s\) 无法确定,请输出 "\(\texttt{! IMPOSSIBLE}\)"
否则,请输出 "\(\texttt{! s}\)"
之后,继续处理下一个测试用例或如果这是最后一个测试用例,则终止程序。打印答案不算作查询。

交互器不是自适应的,这意味着答案在参与者提问之前就已经知道,并不依赖于参与者提出的问题。

如果你的程序为一个测试用例提出了超过 \(n\) 个问题,你的程序应该立即终止以获得错误答案的判定。否则,由于你的解决方案将继续从一个已关闭的流中读取,你可能会得到任意的判定。

在打印查询后,不要忘记输出行结束符并刷新输出。否则,你可能会得到空闲超时的判定。要做到这一点,请使用:

在 C++ 中使用 fflush(stdout) 或 cout.flush();
在 Java 中使用 System.out.flush();
在 Pascal 中使用 flush(output);
在 Python 中使用 stdout.flush();
其他语言请参考相关文档。
样例:
2
5

4

0

1

2

2

0
——————
? 1 5

? 2 4

? 4 5

? 3 5

! 01001

? 1 2

! IMPOSSIBLE
思路:codeforces的div2,div1交互题其实都不会特别难,考验思维。先设置串所有位置为1。显然我每次询问1,r是最佳的,如果第一次返回大于0的值为k,可以知道这个位置必为1,前面k个位置一定为0,因为前面一直返回0是不存在01的情况的,所以只有可能全0全1,或者...1111000...,然后第一次有了值后,如果下次询问值等于上次询问值,则该位置为0,否则为1。只有加0不改变查询值,加1才会使查询值变大

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                     long long
#define lowbit(x) (x & -x)
//#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
	ll ans = 1;
	while (y)
	{
		if (y & 1)
		{
			ans = ans % mod * (x % mod) % mod;
		}
		x = x % mod * (x % mod) % mod;
		y >>= 1;
	}
	return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
	if (y == 0)
		return x;
	else
		return gcd(y, x % y);
}
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
ll q(ll l,ll r)
{
	cout<<"? "<<l<<" "<<r<<endl;
	cout.flush();
	ll x;
	cin>>x;
	return x;
}
ll a[250000];
int main()
{
	fio();
	ll t;
	cin>>t;
	while(t--)
	{
		ll n;
		cin>>n;
		ll cnt=0;
		a[1]=1;
		for(ll i=2;i<=n;i++)
		{
			if(cnt==0)
			{
				ll j=q(1,i);
				if(j==0)
				{
					a[i]=1;
				}
				else  
				{
					cnt=j;
					a[i]=1;
					for(ll k=i-1;k>=i-1-cnt+1;k--)
					{
						a[k]=0;
					}
				//	cout<<a[i]<<endl; 
				}
			}
			else 
			{
				ll j=q(1,i);
				if(j==cnt)
				{
					a[i]=0;
				}
				else 
				{
					cnt=j;
					a[i]=1;
				}
			}
		}
		if(cnt==0)
		{
			cout<<"! IMPOSSIBLE"<<endl;
			continue;
	    }  
		cout<<"! ";
		for(ll i=1;i<=n;i++)
		{
			cout<<a[i];
		}
		cout<<endl;
	}
}

F. Ardent Flames

题面:
你获得了一个新的限定活动角色 Xilonen。你决定在战斗中使用她。

\(n\) 个敌人排成一行。从左边数第 \(i\) 个敌人有 \(h_i\) 的生命值,并且当前位于位置 \(x_i\)。Xilonen 的攻击力是 \(m\),你准备用她来击败敌人。

Xilonen 有一个强大的“地面猛击”攻击。在你进行任何攻击之前,你选择一个整数 \(p\) 并将 Xilonen 放置在那里(\(p\) 可以是任何整数位置,包括已经有敌人的位置)。之后,每次攻击,她对位于位置 \(p\) 的敌人造成 \(m\) 点伤害(如果有的话),对位于位置 \(p-1\)\(p+1\) 的敌人造成 \(m-1\) 点伤害,对位于位置 \(p-2\)\(p+2\) 的敌人造成 \(m-2\) 点伤害,以此类推。距离 Xilonen 至少 \(m\) 的敌人不会受到攻击的伤害。

正式地说,如果有一个敌人位于位置 \(x\),她每次攻击将对那个敌人造成 \(\max(0,m - |p - x|)\) 点伤害。请注意,你不能为不同的攻击选择不同的 \(p\)

在所有可能的 \(p\) 中,输出 Xilonen 必须执行的最少攻击次数,以击败至少 \(k\) 个敌人。如果找不到一个 \(p\),使得最终至少击败 \(k\) 个敌人,输出 \(-1\) 代替。请注意,如果一个敌人的生命值达到 \(0\) 或以下,则认为该敌人已被击败。
输入:
第一行包含一个整数 \(t\)\(1 \leq t \leq 10^4\))——测试用例的数量。

每个测试用例的第一行包含三个整数 \(n\)\(m\)\(k\)\(1 \leq k \leq n \leq 10^5\)\(1 \leq m \leq 10^9\))。

接下来的一行包含 \(n\) 个整数 \(h_1, h_2, ..., h_n\)\(1 \leq h_i \leq 10^9\))。

每个测试用例的最后一行包含 \(n\) 个整数 \(x_1, x_2, ..., x_n\)\(1 \leq x_i \leq 10^9\),对于所有 \(1 \leq i < n\),有 \(x_i < x_{i+1}\))。

保证所有测试用例中 \(n\) 的总和不超过 \(10^5\)
输出:
对于每个测试用例,在新的一行输出一个整数,必须执行的最少攻击次数,以击败至少 \(k\) 个敌人。如果你找不到一个 \(p\),使得最终至少击败 \(k\) 个敌人,输出 \(-1\) 代替。
样例:
6
5 5 3
7 7 7 7 7
1 2 3 4 5
9 5 9
2 4 6 8 10 8 6 4 2
1 2 3 4 5 6 7 8 9
2 10 2
1 1
1 20
2 10 1
69696969 420420420
1 20
2 10 2
10 15
1 19
2 2 2
1000000000 1
1 3
——————
2
2
-1
6969697
15
1000000000
思路:
二分+优先队列+结构体排序。
二分他要攻击多少次mid,然后对于所有值计算它在第mid次攻击死亡每次至少得受到的伤害值,于是可以得出以这个值为边的左右向往m靠近的可能区间,这些区间位置都是满足它刚好在第k回合死亡。
于是这道题变成了区间覆盖问题。所以能找到被覆盖最多的地方就可以了。这里用了优先队列维护右边界,如果下一个数左边界,小于此时队列右边界,则入队它的右边界,否则一直弹出去不行的右边界。取个最大size就行了。如果最大值大于等于k,缩小右边界,否则就缩小左边界。如果最后查询次数等于有右边界,输出-1
想了下,本道题用线段树应该也可以做,如何防爆,动态开点线段树应该就可以了。每次二分完就把数组清空记就好,不过这样就是大材小用了,也快不到哪里去,甚至更慢

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                     long long
#define lowbit(x) (x & -x)
//#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
ll q(ll l,ll r)
{
	cout<<"? "<<l<<" "<<r<<endl;
	cout.flush();
	ll x;
	cin>>x;
	return x;
}
ll a[250000];
ll b[250000];
ll c[250000];
ll n,m,k;
struct s
{
	ll l,r;
}p[4500000];
bool cmp(s x, s y)
{
	if(x.l!=y.l)
	return x.l<y.l;
	else 
	return x.r<y.r;
}
ll ck(ll x)
{
	ll l=0;
	for(ll i=1;i<=n;i++)
	{
		ll u=a[i]/x;
		if(a[i]%x!=0)u++;
		if(u>m)continue;
		else if(u==m) 
		{
			l++;
			p[l].l=p[l].r=b[i];
		}
		else 
		{
			l++;
			p[l].l=b[i]-(m-u);
			p[l].r=b[i]+(m-u);
		}
	}
	sort(p+1,p+1+l,cmp);
	priority_queue<ll,vector<ll>,greater<ll>>q;
	ll ans=0;
	for(ll i=1;i<=l;i++)
	{
		if(q.empty())
		{
			q.push(p[i].r);
		}
		else 
		{
			while(!q.empty()&&q.top()<p[i].l)
			{
				q.pop();
			}
			q.push(p[i].r);
		}
		ans=max(ans,(ll)q.size());
	}
	if(ans>=k)return 1;
	else return 0;
}
int main()
{
	fio();
	ll t;
	cin>>t;
	while(t--)
	{
		cin>>n>>m>>k;
		for(ll i=1;i<=n;i++)cin>>a[i];
		for(ll i=1;i<=n;i++)cin>>b[i];
		ll l=1,r=5e14;
		while(l<r)
		{
			ll mid=(l+r)>>1;
			if(ck(mid))
			r=mid;
			else l=mid+1;
		}
		if(r==5e14)cout<<-1<<endl;
		else 
		cout<<r<<endl;
	}
}

G. Natlan Exploring

题面:
你正在探索令人惊叹的 Natlan 地区!这个地区由 \(n\) 个城市组成,每个城市都有一个吸引力评分 \(a_i\)。如果 \(i < j\)\(\gcd(a_i,a_j)\neq 1\),则从城市 \(i\) 到城市 \(j\) 存在一条有向边,其中 \(\gcd(x, y)\) 表示整数 \(x\)\(y\) 的最大公约数 (GCD)。

从城市 \(1\) 开始,你的任务是确定你可以采取的到达城市 \(n\) 的不同路径的总数,模 \(998,244,353\)。如果访问的城市集合不同,则路径不同。
输入:
第一行包含一个整数 \(n\)\(2 \leq n \leq 2 \cdot 10^5\))——城市的数量。

第二行包含 \(n\) 个整数 \(a_1, a_2, \ldots, a_n\)\(2 \leq a_i \leq 10^6\))——每个城市的吸引力评分。
输出:
输出你可以到达城市 \(n\) 的不同路径的总数,模 \(998\,244\,353\)

样例:
5
2 6 3 4 6
————
5

5
4 196 2662 2197 121
————
2

7
3 6 8 9 11 12 20
————
7

2
2 3
————
0
思路:题目非常简洁,赛时想到了dp+容斥,但是不知道如何容斥。赛后才知道,原来可以用一个数的二进制枚举一个数的因数为质数的集合的所有子集。然后可以发现,如果这个子集的元素个数为奇数它就做正贡献,否则就做负贡献。
于是对于第一个数就先所有可能+1,然后除了最后一个数就是计算它的贡献,计算完后所有子集加上这个贡献就行了,到最后一个数,就只要计算贡献输出就行了。类似于给多个集合算圈定非圈定范围内的数吧,求集合的非交集。
贡献6,6,6,
贡献设为cnt吧
第一个数:2:1,3:1,6:1
第二个数:cnt=1+1-1=1,2:1+1,3:1+1,6:1+1
第三个数cnt=2+2-2,答案为2

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                     long long
#define lowbit(x) (x & -x)
//#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
ll a[200002];
bool st[1000002];
bool c[1000002];
ll dp[1000002];
ll d[1000002];
ll b[100000];
ll fk[1000002];
ll cnt=0;
void ola(ll x)
{
	for (ll i = 2; i <= x; i++)
	{
		if (!st[i])cnt++, b[cnt] = i,c[i]=1;
		for (ll j = 1; b[j] <= x / i; j++)
		{
			st[b[j] * i] = 1;
			if (i % b[j] == 0)
				break;
		}
	}
}
vector<ll>g[200002];
int main()
{
	fio();
	ll t;
	t = 1; ola(1000000);
	while (t--)
	{
		ll n;
		cin >> n;
        ll gs=1;
		ll ans=0;
		for (ll i = 1; i <= n; i++)
		{
			cin >> a[i];
			if(d[a[i]]==0)d[a[i]]=gs,gs++;
			else continue;
			ll u=a[i];
			for(ll j=1;j<=cnt;j++)
			{
				if(u%b[j]==0)
				{
					while(u%b[j]==0)u/=b[j];
					g[d[a[i]]].push_back(b[j]);
				}
				if(c[u])
				{
					g[d[a[i]]].push_back(u);
					break;
				}
				if(u==1)break;
			}
		}
	//	cout<<g[d[6]].size()<<endl;
		for(ll i=1;i<=n;i++)
		{
			if(i==1)
			{
				for(ll j=1;j<=(1ll<<(g[d[a[i]]].size()))-1;j++)
				{
					ll cnt=1;
					ll gs=0;
					for(ll k=0;k<=g[d[a[i]]].size();k++)
					{
						if(j&(1ll<<k))
						{
							cnt*=g[d[a[i]]][k];
							gs++;
						}
					}
					dp[cnt]++;
				}
			}
			else if(i!=n)
			{
				ll f=0;
				for(ll j=1;j<=(1ll<<(g[d[a[i]]].size()))-1;j++)
				{
					ll cnt=1;
					ll gs=0;
					for(ll k=0;k<=g[d[a[i]]].size();k++)
					{
						if(j&(1ll<<k))
						{
							cnt*=g[d[a[i]]][k];
							gs++;
						}
					}
					if(gs%2)
					f=(f%mod+dp[cnt]%mod)%mod;
					else 
					f=(f%mod-dp[cnt]%mod)%mod;
					while(f<0)
					f+=mod;
					f%=mod;
				
				}
				//cout<<f<<endl;
					for(ll j=1;j<=(1ll<<(g[d[a[i]]].size()))-1;j++)
				{
					ll cnt=1;
					ll gs=0;
					for(ll k=0;k<=g[d[a[i]]].size();k++)
					{
						if(j&(1ll<<k))
						{
							cnt*=g[d[a[i]]][k];
							gs++;
						}
					}
					dp[cnt]=(dp[cnt]%mod+f%mod)%mod;
				}
			}
			else 
			{
				for(ll j=1;j<=(1ll<<(g[d[a[i]]].size()))-1;j++)
				{
					ll cnt=1;
					ll gs=0;
					for(ll k=0;k<=g[d[a[i]]].size();k++)
					{
						if(j&(1ll<<k))
						{
							cnt*=g[d[a[i]]][k];
							gs++;
						}
					}
					if(gs%2)
					ans=(ans%mod+dp[cnt]%mod)%mod;
					else 
					ans=(ans%mod-dp[cnt]%mod)%mod;
					while(ans<0)
					ans+=mod;
					ans%=mod;
				}
			}
		}
		cout<<ans<<endl;
	}
}
posted @ 2024-11-18 01:38  长皆  阅读(109)  评论(0编辑  收藏  举报