A. Sasha and Array Coloring
分析:
显然不能一种颜色涂一个数(这样每种颜色对答案的贡献就都是0),一种颜色涂两个数会有最优解
对a数组排序,然后用双指针,每次用最大的数减去最小的数加入到答案当中
C++代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=55;
int a[N];
void solve(){
int n,sum=0;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
for(int i=1,j=n;i<j;i++,j--)sum+=a[j]-a[i];
cout<<sum<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
B. Long Long
分析:
最大可能的数字和一定是所有数的绝对值之和
显然每次遇到第一个数小于0的连续的一串数都小于等于0时,就可以直接改变该区间的符号
从前往后遍历枚举即可
C++代码:
#include<iostream>
using namespace std;
const int N=200010;
typedef long long LL;
int a[N];
int n;
void solve(){
LL sum=0,cnt=0;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],sum+=abs(a[i]);
for(int i=1;i<=n;i++){
if(a[i]<0){
int j=i;
while(j<=n&&a[j]<=0)j++;
i=j-1,cnt++;
}
}
cout<<sum<<" "<<cnt<<'\n';
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
C. Sum in Binary Tree
分析:
按照题中这样编号,每个节点的父节点的编号为该节点的编号的一半
所以,每次给出一个数n就直接加入到答案中去然后除以2,循环操作,直到n为0
时间复杂度为log(n)
C++代码:
#include<iostream>
using namespace std;
typedef long long LL;
void solve(){
LL n,ans=0;
cin>>n;
while(n){
ans+=n;
n/=2;
}
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
D. Apple Tree
分析:
给定一棵树,询问时给出两个节点,问该两个节点中的苹果掉到叶子节点中有几种情况
只需要计算以每个节点为根的子树中有多少个叶子节点,用cnt数组存储,这可以用dfs搜索
然后给定的询问a,b,直接输出cnt[a]*cnt[b]就行了
C++代码:
#include<iostream>
using namespace std;
const int N=200010,M=N*2;
typedef long long LL;
int h[N],e[M],ne[M],idx;
LL cnt[N];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int fa){
cnt[u]=0;
int sum=0;
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(j==fa)continue;
sum++;//子节点数加一
dfs(j,u);
cnt[u]+=cnt[j];
}
if(!sum)cnt[u]=1;//如果子节点数为0,则该节点就是叶子节点,叶子数+1
}
void solve(){
int n,q;
cin>>n;
for(int i=1;i<=n;i++)h[i]=-1;
idx=0;
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
dfs(1,-1);
cin>>q;
while(q--){
int a,b;
cin>>a>>b;
cout<<cnt[a]*cnt[b]<<endl;
}
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
E. Tracking Segments
分析:
找到在哪个改变之后,给定的所有区间至少有一个优美数段
二分所有的变化
对于二分值mid,把所有编号小于等于mid的变化都改变一下,然后记录当前数组的前缀和,这里的前缀和其实就是前缀1的数量。
处理所有的询问,用前缀和直接O(1)算出来当前区间1的个数,如果当前区间1的个数大于0的个数,则直接返回true;没有任何一个区间1的个数大于0的个数,返回false
C++代码:
#include<iostream>
using namespace std;
const int N=100010,INF=0x3f3f3f3f;
typedef pair<int,int> PII;
PII s[N];
int sum[N],a[N],b[N];
int n,m,q,x,cnt;
bool check(int mid){
for(int i=1;i<=n;i++)a[i]=0;
for(int i=1;i<=mid;i++)a[b[i]]=1;
for(int i=1;i<=n;i++)a[i]+=a[i-1];
for(int i=1;i<=cnt;i++){
int l=s[i].first,r=s[i].second;
if((a[r]-a[l-1])*2>r-l+1)return true;
}
return false;
}
void solve(){
cin>>n>>m;
cnt=0;
while(m--){
int l,r;
cin>>l>>r;
s[++cnt]={l,r};
}
cin>>q;
for(int i=1;i<=q;i++)cin>>b[i];
int l=1,r=q+1,ans=INF;
while(l<r){
int mid=l+r>>1;
if(check(mid))ans=min(ans,mid),r=mid;
else l=mid+1;
}
if(r==q+1)cout<<-1<<endl;
else cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
F1. Omsk Metro (simple version)
分析:
所有询问的起点都是u=1,所以问题就是:1~v中是否有一个连续子区间的和为k
我们只需要算出1~v中的最小子段和和最大子段和,然后判断k是否在他俩之间即可
为了能够快速更新新加的节点,我们还要设置一个最小后缀和和最大后缀和
PII s[N];//s[i]={1~i的最小子段和,1~i的最大子段和}
int maxx[N];//maxx[i]:1~i的最大子段和
int minn[N];//minn[i]:1~i的最小子段和假设当前给节点a新加一个子节点idx,节点权值为b,用a的信息更新idx如下:
更新1~idx最大后缀:maxx[idx]=max(b,maxx[a]+b)
更新1~idx最小后缀:minn[idx]=min(b,minn[a]+b)更新1~idx最小子段和:s[idx].x=min(minn[idx],s[a].x)
更新1~idx最大子段和:s[idx].y=max(maxx[idx],s[a].y)
C++代码:
#include<iostream>
#define x first
#define y second
using namespace std;
const int N=200010;
typedef pair<int,int> PII;
int idx;
PII s[N];//s[i]={1~i的最小子段和,1~i的最大子段和}
int maxx[N];//maxx[i]:1~i的最大子段和
int minn[N];//minn[i]:1~i的最小子段和
void solve(){
int n;
cin>>n;
idx=1;
s[1]={0,1};
maxx[1]=minn[1]=1;
for(int i=1;i<=n;i++){
char op;
int a,b,c,t;
cin>>op>>a>>b;
if(op=='+'){
idx++;//节点数加一
maxx[idx]=max(b,maxx[a]+b);//更新1~idx最大后缀
minn[idx]=min(b,minn[a]+b);//更新1~idx最小后缀
s[idx].x=min(minn[idx],s[a].x);//更新1~idx最小子段和
s[idx].y=max(maxx[idx],s[a].y);//更新1~idx最大子段和
}else{
cin>>c;
if(s[b].x<=c&&s[b].y>=c)puts("YES");
else if(a==b&&c==0)puts("YES");
else puts("NO");
}
}
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}