Codeforces Round #773 (Div. 2)简训
导语
稳定三个题,就是第一个题抽风错了很多次,以及队友剩4min痛失第四题
涉及的知识点
STL,思维,字符串
链接:Codeforces Round #773 (Div. 2)
题目
A Hard Way
题目大意:略
思路:一开始做只觉得有边平行x轴就行了,但是还要另一个点在平行边下方
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=2e6+5;
using namespace std;
int t,x[3],y[3];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>t;
while(t--) {
for(int i=0; i<3; i++)
cin >>x[i]>>y[i];
int res=0;
for(int i=0; i<3; i++)
if(y[i]==y[(i+1)%3]) {//平行线
if(y[(i+2)%3]<y[i]) {//另一个在平行线下面
res=abs(x[i]-x[(i+1)%3]);
break;
}
}
cout <<res<<endl;
}
return 0;
}
B Power Walking
题目大意:略
思路:很容易就可以得出结论,首先如果人数小于数字种类,那么一定能使得总和为数字种类,否则就会有新的一个人只拿走一个数字(每个人至少一个),增加总和
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=3e5+5;
using namespace std;
int t,n,a[maxn];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>t;
while(t--) {
cin >>n;
for(int i=1; i<=n; i++)cin >>a[i];
sort(a+1,a+1+n);//排序,方便去重
int len=unique(a+1,a+1+n)-a-1;
for(int i=1; i<=n; i++)
//如果人数超过种类,那么一定会有新的人只拿一个
i>len?cout <<++len<<" ":cout <<len<<" ";
cout <<endl;
}
return 0;
}
C Great Sequence
题目大意:给出长度为n的序列和一个正整数x,判断能否将序列分成多对数,每对数的第一个数乘x等于第二个数,如果不能则判断至少加几个数能使得序列满足条件
思路:题目实际上是一个配对问题,贪心的去考虑,将序列排序,然后从小到大的进行配对,如果没有剩下的就代表需要插入新的数字
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=3e5+5;
using namespace std;
int t,n,a[maxn],x;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>t;
while(t--) {
cin >>n>>x;
map<int,int>use;//统计个数用于配对
for(int i=1; i<=n; i++)cin >>a[i],use[a[i]]++;
int ans=0;
sort(a+1,a+1+n);//必须排序
for(int i=1; i<=n; i++) {
int p=a[i];
if(use[p]==0)continue;//已经用完了
if(use[p*x])
use[p*x]--;
else
ans++;
use[p]--;//
}
cout <<ans<<endl;
}
return 0;
}
D Repetitions Decoding
题目大意:给出一共正整数序列,定义串联重复和相关操作:
- 一个串联重复的序列x长度必须是偶数2k,对前k个元素有 x i = x i + k x_i=x_{i+k} xi=xi+k
- 一个序列a可以被分成多个串联重复每个子段也是串联重复
- 每一次操作可以选择一个数字c然后向已有的序列中的任意位置插入连续两个c
现在判断给定的初始序列能否切割成多个串联重复(在可以操作的前提下),如果能,输出所有操作以及切分出来的串联重复子串长度
思路:如果存在数字出现的次数为奇数,那必然无解
现在来探讨一下如何构造串联重复,利用贪心的思路,每一步构造前面的串,构造完后集体删去,这样的话就可以不用考虑前面的影响而继续获得后面的串,以
[
1
,
3
,
1
,
2
,
2
,
3
]
[1,3,1,2,2,3]
[1,3,1,2,2,3]为例,第一个元素为1,那么找到下一个1,令两者匹配,单独拆出来,也就是
[
1
,
3
,
1
]
[1,3,1]
[1,3,1],想要其串联重复,需要在后一个1后面插3,于是得到
[
1
,
3
,
1
,
3
,
3
]
[1,3,1,3,3]
[1,3,1,3,3],将符合条件的去掉,与剩余的组合得到
[
3
,
2
,
2
,
3
]
[3,2,2,3]
[3,2,2,3],该串本身就是串联重复,因此构造完成,详见代码
代码
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define pii pair<int,int>
using namespace std;
vector<int> vi;
void ins(int p,int c) {
vi.insert(vi.begin()+p,c);
vi.insert(vi.begin()+p,c);
}
void solve() {
vi.clear();
map<int,bool> mii;
vector<pii> q;//记录每次的操作
vector<int> d;//记录每次操作后得到的长度
int n;
scanf("%d",&n);
for(int i = 1; i<=n; i++) {
int x;
scanf("%d",&x);
vi.push_back(x);
mii[x]^=1;//记录是否为偶数
}
map<int,bool>::iterator it=mii.begin();
for(; it!=mii.end(); it++)//判断是否有奇数
if(it->second) {//存在奇数就无解
printf("-1\n");
return ;
}
int i = 0,p=0;
while(i < n) {
for(int j = i+1; j<n; j++) {
if(vi[j]==vi[i]) {//找到两个数字相同
p=j;
for(int k = 1; k < j-i; k++) {
if(j+k>=n||vi[i+k]!=vi[j+k]) {
//j+k>=n是防止因为越界而不能复制
//第二个条件是判断如果不能就复制
ins(j+k,vi[i+k]);//按照题设条件复制
n+=2;
q.push_back(make_pair(j+k,vi[i+k]));
}
}
d.push_back((p-i)*2);//相当于把p~i复制了一遍
break;
}
}
i+=(p-i)*2;
}
cout << q.size() << endl;
for(i = 0; i < q.size(); i++)
cout << q[i].first << " " << q[i].second << endl;
cout << d.size() << endl;
for(i = 0; i < d.size(); i++) {
cout << d[i];
if(i<d.size()-1)
cout << " ";
else
cout << endl;
}
}
int main() {
int t = 1;
scanf("%d",&t);
while(t--)
solve();
return 0;
}
E Anonymity Is Important
题目大意:有一个01序列,但是不清楚哪些是0哪些是1,现在有以下3中操作
- l l l r r r 0 0 0:代表区间 [ l , r ] [l,r] [l,r]全是0
- l l l r r r 1 1 1:代表区间 [ l , r ] [l,r] [l,r]至少有一个1
- q q q i n d e x index index:询问下标为 i n d e x index index的元素是0还是1
输出每次询问的结果(不保证元素的值一定能确定下来)
思路:只有两种方法确定一个元素的值:直接区间全是0或者对于一个包含该元素的区间,其余元素都已确定,而且该区间至少有一个1,那么,不断缩小带1区间就可以更快的得到哪个元素是1,因此可以维护两个信息:未确定元素和带1区间
未确定元素在确定之后需要被删除,使用set维护可以二分查找删除,带1区间的维护使用map,键为左端点,值为右端点,比较相邻区间的右端点可以区间嵌套
代码
#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
int n,q;
set<int>s;
map<int,int>seq;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>n>>q;
for(int i=0; i<=n+1; i++)s.insert(i);
while(q--) {
int t;
cin >>t;
if(!t) {
int l,r,x;
cin >>l>>r>>x;
if(x) {//如果区间内至少存在一个
auto i=seq.lower_bound(l);
//找到区间左端点与l相等或更大的,查看是否被l,r覆盖
if(i!=seq.end()&&i->second<=r)continue;
//如果覆盖了,那么跳过即可,因为区间已经更小了
seq[l]=r;//否则将左端点为l的区间缩小
i=seq.find(l);//查找左端点为l的位置
while(i!=seq.begin()&&r<=prev(i)->second)seq.erase(prev(i));
//如果l的前一个区间包含了l,r,删掉前面一个,因为区间已经缩小了
} else
while(1) {//删除l,r之间的,代表一定为0
auto i=s.lower_bound(l);//二分查找比l大的值
if(*i>r)break;
s.erase(i);
}
} else {
int x;
cin >>x;
if(!s.count(x))cout <<"NO\n";//如果不在,代表已经被排除,也就一定是0
else {//到这里来代表x的情况不确定
int l=*prev(s.find(x)),r=*next(s.find(x));
//找到其所属的最小不确定区间
auto i=seq.upper_bound(l);//查找被l刚好覆盖的左端点
if(i!=seq.end()&&i->first<=x&&i->second<r)cout <<"YES\n";
//x在这个区间里,而这个区间里只有x没有确定下来(未确定的l和r不在这个区间内)
else cout <<"N/A\n";
}
}
}
return 0;
}