目录
D.Burenka and Traditions (easy version&&hard version)
前言
由于笔者为acm初学者,写博客主要目的是记录自己的成长历程另一方面是希望能给大家贡献一些不一样的思路和实现方法帮到大家。(每次争取补题补到e,f就不看了hhh)
C. Fighting Tournament
https://codeforces.com/contest/1719/problem/C
题目:
题目大意:有n名选手,每名选手的力量值为ai,且题目保证这n名选手的力量值为1-n的一个全排列,进行无限轮比赛,每次比赛力量值较大者获胜。给出q个询问:第i个选手前k轮比赛赢了多少场?
题目思路:根据题意我们知道当力量值为n的选手为队首时他将一直赢下去,因此我们仅需模拟至多n-1轮比赛直到力量值为n的选手为止,记录此时场次为locn。用vector容器存下除了最大力气以外的每位选手取得胜利时的场次,当询问非最大力量值时使用upper_bound二分查找第一个大于场次k的胜场即可得出此时赢了多少场(因为此函数返回的是找到的那个点的迭代器因此我们减去第一个点的迭代器就是此时赢了多少场次),如果询问的是最大力量值选手我们只需判断k是否大于等于locn,如果小于就是0,如果大于等于就是赢了k-locn+1场次。
代码如下(示例):
#include<iostream>//c++支持动态数组我也是看了几个大佬代码才知道的。。。
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
void solve(){
int n,q;cin >> n >> q;
vector<int> vec[n+1];//存每个力量值选手的胜利场次
queue<int> dq;//队列模拟前n-1轮比赛
int id[n+1];//存每个id对应的力量值这样当我们询问的时候我们可以直接将i对应为力量值
for(int i=1;i<=n;i++){
int x;cin >> x;
dq.push(x);
id[i]=x;
}
int locn=0;
int head=dq.front();dq.pop();
if(head==n){//如果力量值最大的是第一个直接单独列出讨论
while(q--){
int i,k;
cin >> i >> k;
i=id[i];
if(i==n)cout<< k<<endl;
else cout<< 0<< endl;
}
}else{
while(head!=n){
++locn;//统计当前场次
int temp=dq.front();dq.pop();
head=max(head,temp);//更新选手
vec[head].push_back(locn);//记录胜场
}
while(q--){
int i,k;cin >>i>>k;
i=id[i];
if(i==n){
cout<<max(0,k-locn+1)<<endl;//locn最后是力量值为n的选手赢得第一场的场次
}else{
cout<<upper_bound(vec[i].begin(),vec[i].end(),k)-vec[i].begin() <<endl;//二分找场次所在区间
}
}
}
}
int main(){
int t;
cin >> t;
while(t--)
solve();
return 0;
}
D.Burenka and Traditions (easy version&&hard version)
https://codeforces.com/contest/1719/problem/D2
题目:
题目大意:给定n个整数,我们有以下操作:选取一个区间l,r和一个整数x使得下标为l至r的数均与x进行异或,每次操作的代价是(r-l+1)上取整,问要使所有数全部变为0至少需要多少步操作?
题目思路:
1.注意到我们选取三个以上的数作为区间时和选取若干个两个数作为一个区间外加一个数一个区间的时间代价是一样的
2.最差的结果是每次异或自己且异或之后旁边的数没有变为0,时间代价是n。
3.如果我们发现一段数满足al^al+1^al+2^...^ar==0那么我们的答案就可以减小1
4.我们用set维护从头开始的异或和如果此时异或和之前已经出现过则说明找到了一段,清空set继续找下一段(贪心考虑这样保证我们找到的区间是最多的)。
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
int n;
void solve(){
cin >> n;
int cnt=0;
int t=0;
set<int> st;
st.insert(0);
for(int i=1;i<=n;i++){
int x;cin >> x;
t^=x;
if(st.count(t)){
st.clear();t=0;st.insert(0);
}else{
cnt++;
st.insert(t);
}
}
cout<< cnt<<endl;
}
int main(){
int t;
cin >> t;
while(t--){
solve();
}
return 0;
}
E. Fibonacci Strings
题目大意: 给定k个不同的字母组成的字符串,给定每个字母出现的次数,询问是否存在一种排列方式使得这个字符串成为斐波那契字符串:要求每一部分的字母出现的次数符合斐波那契数列。
思路:每次贪心的选取剩余最多的那个字母,如果和上一个是同一个字母那么用第二多的那个字母。先算出所有字母的总数量和,lower_bound查找是否有斐波那契数列和等于总数量,如果找不到输出no,用优先队列模拟,如果出现最多的那个字母都小于当前斐波那契数输出no,如果最后模拟完成输出yes。至于为什么这样贪心是对的tutorial里面有官方证明各位可以去看一下。证明链接:
https://codeforces.com/blog/entry/106049
代码如下:
#include<iostream>
#include<set>
#include<queue>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
const int N=60;
LL f[N],s[N];
void get_f(){
f[1]=1;f[2]=1;
for(int i=3;i<=N-10;i++)
f[i]=f[i-1]+f[i-2];//预处理斐波那契
for(int i=1;i<=44;i++)
s[i]=s[i-1]+f[i];//预处理斐波那契数列和
}
void solve(){
int n;cin >> n;
priority_queue<PII> q;
int clo=0;
LL res=0;
for(int i=1;i<=n;i++){
int x;cin >> x;
q.push({x,++clo});//用pair存入优先队列,第一个是数量,第二个是字母类型
res+=x;
}
int loc=lower_bound(s+1,s+45,res)-s;//二分查找是否有s等于res
if(s[loc]!=res)puts("NO");
else{
int pre=0;//存上一个字母类型
for(int i=loc;i>=1;i--){
LL now=f[i];
PII maxclo=q.top();q.pop();
if(maxclo.second==pre){//这里防止一共就一种字母pop两次会报错,需要特判一下
if(q.empty()){
puts("NO");
return ;
}
PII maxclo1=maxclo;
maxclo=q.top();q.pop();//如果字母和上一段一样找第二多的字母
q.push(maxclo1);
}
if(maxclo.first<now){//如果不够输出no
puts("NO");
return ;
}else{
maxclo.first-=now;
pre=maxclo.second;
q.push(maxclo);
}
}
puts("YES");//如果顺利循环结束输出yes
}
return ;
}
int main(){
int t;
cin >> t;
get_f();
while(t--){
solve();
}
}