C. Choose the Different Ones!
给定一个由 n 个整数组成的数组 a,一个由 m 个整数组成的数组 b 和一个偶数 k 。
你的任务是从这两个数组中精确选择 k/2 个元素,使所选择的元素中包含从 1 到 k 的每个整数。
开三个map,第一个存A数组中1<=a[i]<=k的数,第二个存B数组1<=b[i]<=k的数,第三个存这两个的总个数。如果,mapA.size()>=k/2 and mapB.size()>=k/2 and mapSum.size()>=k就输出YES,else 输出NO。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
void solve(){
int n,m,k;
cin>>n>>m>>k;
vector<int>a(n);
vector<int>b(m);
map<int,int>u1;
map<int,int>u2;
map<int,int>u;
for(int i=0;i<n;i++){
cin>>a[i];
if(a[i]>=1 and a[i]<=k){
u1[a[i]]++;
u[a[i]]++;
}
}
for(int i=0;i<m;i++){
cin>>b[i];
if(b[i]>=1 and b[i]<=k){
u2[b[i]]++;
u[b[i]]++;
}
}
if(u2.size()>=k/2 and u1.size()>=k/2 and u.size()>=k){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
cin>>oyyo;
while(oyyo--) {
solve();
}
return 0;
}
D. Find the Different Ones!
给你n个数字,m组查询,每次查询中是否有和并且,如果有输出不同数字的坐标,没有输出两个-1。
数据范围是2*10^5,考虑nlogn以内复杂度,从后向前处理最大的连续相等距离,进行O(n)遍历,O(1)查询。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
int pos[1000006];
void solve(){
int n;
cin>>n;
vector<int>a(n);
for(int i=0;i<n;i++){
cin>>a[i];
}
pos[n-1]=n;//记录最大的连续相等字串长度,最右边的边界是n.
for(int i=n-2;i>=0;i--){//倒序遍历
if(a[i]==a[i+1]){
pos[i]=pos[i+1];//如果这个数和前一个数相同,那么就继承他的最大扩展边界,
}else{
pos[i]=i+1;//否则的话,字串连续从他这里更新。
}
}
int q;
cin>>q;
while(q--){
int l,r;
cin>>l>>r;
l--,r--;
if(pos[l]>r){//如果输入的有边界小于处理后的连续范围边界,那么这一段就是连续的。
cout<<-1<<" "<<-1<<endl;
}else{
//否则的话,再所给范围内连续就会中断,直接输出最左边和pos[l](他所达到的边界)就好了。
cout<<l+1<<" "<<pos[l]+1<<endl;
}
}
cout<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
cin>>oyyo;
while(oyyo--) {
solve();
}
return 0;
}
E.Klever Permutation
给你两个数字n和k,问每个的最大值和最小值相差要小于等于1。输出合法序列。
一次移动1最好,这样永远不会相差超过1,那么第一轮就取右边界一直减,第二轮就取左边界一直加。直到l>=r跳出循环。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
int a[200005],b[200005];
void solve(){
int n,m;
cin>>n>>m;
int l=1,r=n;
int rt=1;//记录每次赋值的开头数,每次从这个开头错落赋值。
while(1){
if(l>r){
break;
}
for(int i=rt;i<=n;i+=m){
a[i]=r;//从大开始排
r--;
}
rt++;
if(l>r){
break;
}
for(int i=rt;i<=n;i+=m){
a[i]=l;//从小开始排
l++;
}
rt++;//开头赋值点坐标++,
}
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
cin>>oyyo;
while(oyyo--) {
solve();
}
return 0;
}
D. Rudolf and the Ball Game
鲁道夫和他的健忘朋友。
这个提要良好的使用set容器。【总结】C++ 基础数据结构 —— STL之集合(set)用法详解_std::set-CSDN博客
C++ 之 Vector数组基础用法介绍_c++ vector数组-CSDN博客
(从第二个博客里学的新知识)
这个在一循环进行外(游戏开始前),要先创建一个记录现在可能拿到球的人的数组。
之后进入循环,如果第二字符为’0‘,那么就正序传递并循环(%取模运算),在内部创建一个set集合,用来存储传递过程中可能是接球者的人。每一次判断完后,now数组都要清空,然后把now数组更新为现在的set里的数据,(每次传递继承上一次传递的结果)。等到m次判断完后,现在的now就是最后方案了,直接遍历输出就好了。
这一层的复制起始是没必要的,因为原题解写了,可能是怕有改动的错误。但是这个只是不会。
细节:逆时针旋转要加n再取模。如果是’?‘两个情况都要包含。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
void solve(){
int n,m,x;
cin>>n>>m>>x;//球yuan,投球次数,首个人
vector<int>now(1,x);
while(m--){
int r;
char c;
set<int>s;
vector<int>mid(now);//复制构造函数相当于vector<int>mid=now;
cin>>r>>c;
//set 是一个有序的容器,里面的元素都是排序好的,默认从小到大。
if(c=='0'){
for(int c:now){
if(c+r==n){
s.insert(n);
}else{
s.insert((c+r)%n);
}
}
}else if(c=='1'){
for(int c:now){
if(c-r==0){
s.insert(n);
}else{
s.insert((c-r+n)%n);
}
}
}else{
for(int c:now){
if(c+r==n){
s.insert(n);
}else{
s.insert((c+r)%n);
}
if(c-r==0){
s.insert(n);
}else{
s.insert((c-r+n)%n);
}
}
}
now.clear();
for(int c:s){
now.emplace_back(c);
}
}
cout<<now.size()<<endl;
for(int c:now){
cout<<c<<" ";
}
cout<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
cin>>oyyo;
while(oyyo--) {
solve();
}
return 0;
}
E. Rudolf and k Bridges
题目要求连续造k座桥,那么只需要把每一座桥的建造成本记录一下,最后取其中和最小的连续k座桥的成本即可。
对于每一座桥计算他的建造成本:用dp来做,这座桥的第j个位置的建造成本为dp[j];
dp[j]=dp[k]+a[i][j]+1;其中k是一个范围[j-k-1,j-1];
最后就是一个前缀和来求连续k座桥的成本了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
void solve(){
int n,m,k,d;
cin>>n>>m>>k>>d;//桥数,支架间最大距离。
vector<vector<int>>a(n,vector<int>(m));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
}
}
vector<int>sum;
for(int i=0;i<n;i++){
deque<int>q;
vector<int>dp(m+1);
q.push_back(0);//先将陆地上第一个支架放入队列。
dp[0]=1;//地面上的起点坐标为0,深度也为0,所以起点的价钱是1。
for(int j=1;j<m;j++){
while(!q.empty() and j-q.front()-1>d){
q.pop_front();
//对于队列的开头,如果两个点的差值大于最大间隔,这个点就没用了,对于之后点的更没用,所以直接弹出。
}
dp[j]=dp[q.front()]+a[i][j]+1;
//这个点的价钱更新。
while(!q.empty() and dp[j]<dp[q.back()]){
q.pop_back();//比新来的弱的也直接退掉。 保证下次更新时,都可以是最便宜的价格
}
q.push_back(j);//这个点建支架了。插入进去。
}
sum.emplace_back(dp[m-1]);//当到末尾的时候,在sum数组里插入最大价格。
}
//两个类似的方法求连续的最小和。
// int pre[n+1];
// pre[0]=sum[0];
// for(int i=1;i<sum.size();i++){
// pre[i]=pre[i-1]+sum[i];
// }
// int ss=pre[k-1];
// for(int i=k;i<sum.size();i++){
// ss=min(ss,pre[i]-pre[i-k]);
// }
// cout<<ss<<endl;
if(sum.size()==1){
cout<<sum[0]<<endl;
return;
}
for(int i=1;i<n;i++){
sum[i]+=sum[i-1];
}
int min1=sum[k-1];//把第一个连续K串统计进去,要不会丢掉
for(int i=k;i<n;i++){
min1=min(min1,sum[i]-sum[i-k]);
}
cout<<min1<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int oyyo=1;
cin>>oyyo;
while(oyyo--) {
solve();
}
return 0;
}
B. A BIT of a Construction
如果给定的n是1的话,我们没有选择,只能去输出k,如果这个数本身就是2^n-1这样二进制全是1的话我们也直接输出k就好,剩下的所有数全都输出0,加和也不影响。那对于一般情况,我们去计算他的二进制位有几个,然后去输出比它小的满足2^n-1形式的最大数。在输出k-这个数,这就是我们能找到的二进制位1最多的情况,然后其他全部输出0就好。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int,int>
#define endl '\n'
int q_pow(int a, int b)
{
int res = 1;
while(b)
{
if(b&1) res *= a;
a *= a;
b >>= 1;
}
return res;
}
int lcm(int a, int b)
{
return a*b/__gcd(a,b);
}
void solve()
{
int n,k;
cin>>n>>k;
if(n==1)
{
cout<<k<<endl;
return;
}
int cnt0=0;
int t=k;
int len=0;
while(t)
{
if(t%2==0)
{
cnt0++;
}
t/=2;
len++;
}
if(cnt0==0)
{
cout<<k<<" ";
for(int i=1;i<n;i++)
{
cout<<0<<" ";
}
cout<<endl;
return;
}
int st=(1<<(len-1))-1;
cout<<st<<" "<<k-st<<" ";
for(int i=2;i<n;i++)
{
cout<<0<<" ";
}
cout<<endl;
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int T;
cin >> T;
// T = 1;
while(T--)
{
solve();
}
return 0;
}
C. How Does the Rook Move?
当一个n*n的棋盘上放上一个棋子的时候,整行整列的期满都无法被访问,因此棋盘退化为一个(n-1)*(n-1)的棋盘,可以用dp来对答案进行求解,由于电脑会接续你的步骤,那么当你下了(r,c)(r!=c)时,整个棋盘就变成了(n-2)*(n-2)棋盘当你下了(r,c)(r==c)时,电脑无法理解你的操作,会跳过。那么棋盘就会变成一个(n-1)*(n-1)的棋盘,只需要考虑求解就好了,假设第一行第一列是新增的。然后思考第一步放在每个地方的情况,在(1,1)情况下会退化为(i*i)的棋盘,其他情况都是(i-1)*(i-1)的棋盘,所以状态转移方程是dp[i]=dp[i-1]+(i+i-2)*dp[i-2]。然后根须输出判断出k不之后退化为多大的棋盘就好了,dp[i]记录的是 i*i 的棋盘有多少方案数。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
priority_queue<int,vector<int>,greater<int>>qmi;//大根堆
priority_queue<int>qma;//小根堆
const int mod = 1e09+7;
const int N=5e5+10;
int n,k;
vector<int>a(N,0);
vector<int>dp(N,0);
void solve(){
cin>>n>>k;
for(int i=0;i<k;i++)
{
int x,y;
cin>>x>>y;
if(x==y)
{
n-=1;
}else
{
n-=2;
}
}
cout<<dp[n]<<endl;
}
signed main() {
int t;
cin>>t;
//t=1;
dp[0]=1;
dp[1]=1;
dp[2]=3;
for(int i=3;i<N;i++)
{
dp[i]=1*dp[i-1]+(i-1)*2*dp[i-2];
dp[i]%=mod;
}
while(t--) {
solve();
}
return 0;
}
C. Everything Nim
首先,如果是单纯的自然数排序,那么他只会是从第到高依次选其每次只会选1(后面的数会因为前面的选择产生新的1),而如果序列中第一个没出现的数是一个奇数,那么在这个数之后就会是Alice来操作。而Alice可以来操作数的节奏,决定每一个数是剩余1,还是剩余0,最后导致Alice赢,如果是偶数,那么就是Bob赢因为这样就是他来调控比赛的节奏了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
priority_queue<int,vector<int>,greater<int>>qmi;//大根堆
priority_queue<int>qma;//小根堆
const int mod = 1e09+7;
const int maxn = 100+10;
int gcd(int a,int b)
{
return b>0?gcd(b,a%b):a;
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
bool cmp(char a,char b)
{
return a>b;
}
const int N=1e7+10;
void solve(){
int n;
cin>>n;
vector<int>a(n);
int max1=0;
unordered_map<int,int>u;
for(int i=0;i<n;i++)
{
cin>>a[i];
max1=max(max1,a[i]);
u[a[i]]++;
}
int max2;
for(int i=1;i<=max1+1;i++)
{
if(u[i]==0)
{
max2=i;
break;
}
}
if(max2>max1)
{
if(max1%2==0)
{
cout<<"Bob"<<endl;
}else
{
cout<<"Alice"<<endl;
}
return ;
}
if(max2%2==1)
{
cout<<"Alice"<<endl;
}else{
cout<<"Bob"<<endl;
}
}
signed main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}