Codeforces Round #746 (Div. 2)简训
导语
日常
涉及的知识点
贪心,思维,排序,位运算,树,DFS序
链接:Codeforces Round #746 (Div. 2)
题目
A Gamer Hemose
题目大意:n把武器,每把武器有自己的伤害值,敌人有H的血量,每次遍历这n把武器,可以选择任意武器进行攻击,如果遍历一次后敌人血量大于0,重新遍历选择,不能连续两次选择同一把武器,询问最少攻击多少次能使敌人血量小于等于0
思路:选取最大值和次大值直接交替即可,直接除法运算
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4;
int t,n,h,a[maxn];
int main() {
scanf("%d",&t);
while(t--) {
int ans=0,bigger=0,posber=0,big=0;
scanf("%d%d",&n,&h);
for(int i=1; i<=n; i++) {
scanf("%d",a+i);
if(a[i]>bigger) {
bigger=a[i];
posber=i;
}
}
for(int i=1; i<=n; i++)
if(a[i]>big&&i!=posber)
big=a[i];
int sum=big+bigger;
ans=h/sum;
h-=sum*ans;
ans*=2;
if(h>bigger)
ans+=2;
else if(h>0)
ans++;
printf("%d\n",ans);
}
return 0;
}
B Hemose Shopping
题目大意:给出n个乱序整数构成的序列,限定交换的步长至少为x,判断是否能经过有限次交换使得序列变成非递减序序列
思路:经过推导得出的结论:当步长≤n/2,序列中任意一个值可以到达任意位置,当步长大于n/2,序列中n-x+1–x是无法被交换的,也就是说这个区间内的所有元素必须一开始就是满足条件的,为了方便可以先将整个序列排序,之后判断对应区间内的元素有无变化即可
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,x,a[maxn],t,b[maxn];
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&x);
for(int i=1; i<=n; i++) {
scanf("%d",a+i);
b[i]=a[i];
}
if(x<=(n/2)) {
printf("YES\n");
continue;
}
sort(b+1,b+1+n);//排成有序
bool flag=0;
for(int i=n-x+1; i<=x; i++)//区间判断
if(a[i]!=b[i]) {
printf("NO\n");
flag=1;
break;
}
if(!flag)printf("YES\n");
}
return 0;
}
C Bakry and Partitioning
题目大意:给定一棵树以及点权,判断是否能切1–k-1刀使得划分的部分各自异或和相等
思路:首先,异或和有一个这样的性质,如果异或和为0,那么必定是两个相同的数异或的结果,先求出整棵树的异或和,如果为0,则任意一刀都符合条件,如果不为0,则判断整棵树能否分为奇数个异或和相同的部分(或一个为最后结果,其余为0),详见代码
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int t,n,head[maxn],cnt,a[maxn],sum[maxn],res,ans,k;
bool vis[maxn];
struct node {
int next,to;
} e[maxn];
void Add(int from,int to) {
e[++cnt].to=to;
e[cnt].next=head[from];
head[from]=cnt;
}
void DFS(int u) {
vis[u]=1;
sum[u]=a[u];
for(int i=head[u]; i; i=e[i].next) {
int v=e[i].to;
if(!vis[v]) {
DFS(v);
sum[u]^=sum[v];
}
}
if(sum[u]==res) {
ans++;
sum[u]=0;
}
vis[u]=0;
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&k);
res=ans=cnt=0;
memset(head,0,sizeof(head));
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
res^=a[i];
}
for(int i=1; i<=n-1; i++) {
int u,v;
scanf("%d%d",&u,&v);
Add(u,v);
Add(v,u);
}
if(res==0) {
printf("YES\n");
continue;
}
DFS(1);
if(ans>=3&&k>=3)
printf("YES\n");
else
printf("NO\n");
}
//system("pause");
return 0;
}
D Hemose in ICPC ?
题目大意:给出一个树,有边权,但边权未知,规定两点间距为两点路径边权值gcd,每次可以询问一个点集里的可获得的最大距离,求整棵树中能使得距离最大的两点
思路:本题比较特殊,与其说是求得距离,不如说是根据输入的数据构造距离,因为是取gcd,所以贪心的想,直接以相邻两点来计算便可获得最大值,也就是最后答案是相邻点,所以关键是求出最大值的两个端点,其余参考代码和参考文献,思路太巧了,看懂了之后觉得也不难,但是很棒
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int t,n,head[maxn],cnt,seq[maxn],top,ans;
bool vis[maxn];
vector<int>q;
struct node {
int next,to;
} e[maxn];
void Add(int from,int to) {//链式前向星
e[++cnt].to=to;
e[cnt].next=head[from];
head[from]=cnt;
}
void DFS(int u,int f) {//确定欧拉序
for(int i=head[u]; i; i=e[i].next) {
int v=e[i].to;
if(v==f)continue;
seq[++top]=v;
DFS(v,u);
seq[++top]=u;
}
}
int Query() {//确定询问点集
sort(q.begin(),q.end());
q.erase(unique(q.begin(),q.end()),q.end());
cout <<"? "<<q.size()<<" ";
for(int i=0; i<q.size(); i++)cout <<q[i]<<" ";
cout <<endl;
q.clear();
int t;
cin >>t;
return t;
}
int main() {
int n;
scanf("%d",&n);
for(int i=1; i<n; i++) {
int u,v;
scanf("%d%d",&u,&v);
Add(u,v);
Add(v,u);
}
DFS(1,0);
for(int i=1; i<=n; i++)q.push_back(i);
ans=Query();//获得整个的最大值
int l=1,r=top;
while(l<r-1) {//必须确定到两个端点
int mid=(l+r)>>1;
for(int i=l; i<=mid; i++)q.push_back(seq[i]);
int t=Query();//获得区间最值
if(t==ans)r=mid;//二分
else l=mid;
}
cout <<"! "<<seq[l]<<" "<<seq[r]<<endl;
return 0;
}