Codeforces Round #633 (Div. 2)
A. Filling Diamonds
题意:
(交的时候也没搞懂这个题意,犹豫好久)用一个菱形覆盖一个"4n−2triangles"的不同覆盖方法
思路:
可以看出来有n种覆盖方法
代码:
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<set>
#include<map>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
const int N=100010;
int main(){
int T;
cin>>T;
while(T--){
int n;
cin>>n;
cout<<n<<endl;
}
return 0;
}
B. Sorted Adjacent Differences
题意:
给出一个长度为n的序列:\(a_1,a_2,…,a_n\),构造一种序列重排后满足:\(|a_1−a_2|≤|a_2−a_3|≤…≤|a\)n-1\(−a_n|\),输出任意一种方案。
思路:
这种题我们一般都先按从小到大排序,然后找到某种合适的顺序。排序后可以这样构造:\(a_1\)和\(a_n\)的差值是最大的,然后让\(a_1,a_n\)排到最后,\(a_1\)和\(a\)n-1的差小于等于\(a_1\)和\(a_n\)的差,但是大于不包含\(a_n\)的其他所有。所有用双指针从前和从后接替加入。
代码:
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<set>
#include<map>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
const int N=100010;
int a[N];
vector<int> v;
int main(){
int T,n;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
sort(a+1,a+1+n);
v.clear();
for(int i=1;i<=n/2;++i){
v.push_back(a[i]);
v.push_back(a[n-i+1]);
}
if(n%2){
v.push_back(a[n/2+1]);
}
reverse(v.begin(),v.end());
for(auto it: v) cout<<it<<" ";
cout<<endl;
}
return 0;
}
C. Powered Addition
题意:
给出一个序列长度为n的序列,然后可以进行无数次操作,第i次可以选择任意个数(可以一个都不选),将其加上2i-1,求将序列变成非降序序列的最少操作次数。 \(1≤n≤10^5,−10^9≤a_i≤10^9\)
思路:
我们让\(a[i-1]>a[i],t=a[i-1]-a[i]\),t的二进制最高位1的位数就是我们使得a[i-1]<=a[i]的最小操作次数,所以当我们让a[i-1]=a[i]使用的是最小操作次数,且对于后面的限制更小,所以枚举所有不合法的前后差值,更新最高位1的位数就是答案。
代码:
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<set>
#include<map>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
long long a[100010],p[60];
int main(){
int T;
p[0]=1;
for(int i=1;i<60;++i){
p[i]=p[i-1]*2;
}
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%lld",&a[i]);
}
int ans=0;
for(int i=2;i<=n;++i){
if(a[i-1]>a[i]){
LL t=a[i-1]-a[i];
int res=0;
//cout<<t<<endl;
while(t){
res++;
t>>=1;
}
ans=max(ans,res);
a[i]=a[i-1];
}
}
cout<<ans<<endl;
}
return 0;
}
D. Edge Weight Assignment
题意:
给出一个无权树,给每个边上赋一个权值,使得每两个叶子节点之间的路径边权异或和位0,求权值数的种类的最少和最多各为多少。\(3≤n≤10^5\)
思路:
数最少的情况:
当两个叶子节点之间的边数为偶数,那么这条路上的只用一种数就可以了。当边数奇数时,我们也只需要三个数就可以了:假设a-b之间边数为奇数,对于这一条单一的路径至少需要三个数是显然的,假设这条路径上有别另一条分支点d引出叶子节点c,c到d的边可以构造成异或和等于a到d的异或和,这样c到a的异或和为0,由于c到b和a到b一样,所以c到b异或和也为0.
数最多的情况:
若n-1条边权值都不相等,假设a到b的m条边都不相等,异或和为0,若有a的父亲节点d有另一个儿子节点c也是叶子,那么a到b的边数是2,则a到d和c到d的边权值相等,所以遍历一遍减去满足这种情况的边数。
代码:
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<set>
#include<map>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
const int N=100010;
vector<int> g[N];
int dis[N],ans1,ans2;
bool dfs1(int u,int fa){
dis[u]=dis[fa]+1;
if(dis[u]%2&&g[u].size()==1) {
ans1=3;
//cout<<u<<endl;
return true;
}
for(int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v==fa) continue;
if(dfs1(v,u)) return true;
}
return false;
}
void dfs2(int u,int fa){
int cnt=0;
for(int i=0;i<g[u].size();++i){
int v=g[u][i];
if(fa==v) continue;
if(g[v].size()==1) cnt++;
else dfs2(v,u);
}
if(cnt>0){
ans2-=cnt-1;
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1,u,v;i<n;++i) {
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
ans1=1,ans2=n-1;
int f1=0,f2=0;
for(int i=1;i<=n;++i){
if(g[i].size()==1&&!f1){
dis[0]=-1;
dfs1(i,0);f1=1;
}
if(g[i].size()>1&&!f2) {
dfs2(i,0);f2=1;
}
}
cout<<ans1<<" "<<ans2<<endl;
return 0;
}