A. Find Array
题意:
有一朵花初始1厘米高,给出n天是否浇水的情况。要求求出最后花朵的高度
如果花朵死亡,则返回-1
- 连续两天不浇水死亡
- 今天浇水昨天没浇水张高1厘米
- 今天浇水了昨天也浇水了张高5厘米
- 今天没浇水,不长高
分析:
按照题意模拟即可
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
int n;cin>>n;
int pre = -1;
int cur = 1;
int die = 0;
for (int i=1;i<=n;++i)
{
int sta;cin>>sta;
if (sta==0&&pre==0)
die = 1;
else if (sta==1&&pre==1)
cur+=5;
else if (sta==1)
cur+=1;
pre = sta;
}
if (die)cout<<"-1\n";
else cout<<cur<<endl;
}
}
B. Array Eversion
题意:
给一个长为n的数组a
定义转置操作:
选取数字ana_nan,将数组a中所有≤an\le a_n≤an的数字放在左边
所有≥an\ge a_n≥an的数字放在右边
两边数字与数字之间原本的顺序保持不变
换句话说,转置操作是稳定的
要求你计算,在第几次之后的转置操作不会再改变数组a
分析:
如果ana_nan为最大值的话,那就不可能改变数组a了
因此,我们要计算过了几次之后ana_nan会变成最大值
按照稳定的排序的想法,进行一次转置操作之后,新的ana_nan是距离原本ana_nan最近的
第一个大于他的值
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
int a[maxn];
int n;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
cin>>n;
for (int i=1;i<=n;++i)cin>>a[i];
int cur = a[n];
int ans = 0;
for (int i=n-1;i>=1;--i)if (a[i]>cur)
{
++ans;
cur = a[i];
}cout<<ans<<endl;
}
}
C. Minimize Distance
题意:
数轴上,坐标0处有nnn个货物
坐标轴上有nnn个仓库,你要给每个仓库分配一个货物
刚开始你在坐标0上,你一次最多可以拿kkk个货物,请问你最短需要走多远的路?
不要求最后返回坐标0
分析:
先不考虑负轴。
对于仓库1,2,3,4,51,2,3,4,51,2,3,4,5
k=2k=2k=2
一个贪心的想法是,我们先填1,21,21,2再填3,43,43,4,然后最后单独填555
这样的花费为:2×2+2×4+5=172\times 2+2\times 4+5=172×2+2×4+5=17
很明显,有更好的策略
我们可以:先填111,再填2,32,32,3,最后填4,54,54,5
花费为:2×1+2×3+5=132\times 1+2\times 3+5=132×1+2×3+5=13
分别单独对正轴和负轴实行上述策略即可。
然后因为,我们最后选择是呆在最左边还是最右边取决于两边绝对值的大小
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int a[maxn];
int n,k;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
cin>>n>>k;
for (int i=1;i<=n;++i)cin>>a[i];
sort(a+1,a+1+n);
ll ans = 0;
for (int i=1;i<=n&&a[i]<0;i+=k)
ans += 2*abs(a[i]);
for (int i=n;i>0&&a[i]>0;i-=k)
ans += 2*a[i];
ans -= max(abs(a[1]), abs(a[n]));
cout<<ans<<endl;
}
}
D. Yet Another Sorting Problem
题意:
给你个长为n的数组,你之可以进行3−cycle3-cycle3−cycle变换,问是否可以实现排序
3−cycle3-cycle3−cycle的定义是:
选取i,j,ki,j,ki,j,k互不相等,然后让a[i]→a[j]→a[k]→a[i]a[i]\rightarrow a[j]\rightarrow a[k]\rightarrow a[i]a[i]→a[j]→a[k]→a[i]
分析:
拿到现有的数组之后a
我们得到一个排序后的数组b
按照b我们可以得知a数组中的每一个数aia_iai应该到哪一个索引jjj
因此,可以建成一张图
也就是,一个个环。
分奇偶环考虑
考虑奇环,对于长度为1,31,31,3的奇环,显然可以排序成功
对于长度为555的奇环,例如数组:2,3,4,5,12,3,4,5,12,3,4,5,1
环为:1→2→3→4→5→11\rightarrow 2\rightarrow 3\rightarrow 4\rightarrow 5\rightarrow 11→2→3→4→5→1
假设我们选取i=3,j=4,k=5i=3,j=4,k=5i=3,j=4,k=5
那么,数组变为:2,3,1,4,52,3,1,4,52,3,1,4,5
只剩下一个长为333的奇环。
也就是说,对于任意的奇环,我们都可以−2,−2,…,−2-2,-2,\dots,-2−2,−2,…,−2地使得他们变成长度只有333的奇环,从而实现排序
偶环的情况下:
观察所给样例:
2,1,4,32,1,4,32,1,4,3
这里两个偶环的情况下是可以实现排序的
原因是,两个偶环可以一次3−cycle3-cycle3−cycle操作变成一个奇环
因此,如果有偶环则一定要使得偶环的数目是偶数
而如果数组a中有重复的元素的话,我们划分的偶数环的个数是不一样的
我们可以通过重复元素,来调节偶数环的个数,从而导致一定可以排序成功!
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;
int G[maxn];
int a[maxn];
int vis[maxn];
int n;
int main()
{
ios::sync_with_stdio(0);
int T;cin>>T;
while (T--)
{
cin>>n;
for (int i=1;i<=n;++i)vis[i]=G[i]=0;
bool f = false;
for (int i=1;i<=n;++i)
{
cin>>a[i];
if (G[a[i]]!=0)f=true;
G[a[i]]=i;
}
if (n==1)
{
cout<<"YES\n";
continue;
}else if(n==2)
{
if (a[1]<=a[2])cout<<"YES\n";
else cout<<"NO\n";
continue;
}
if (f)
{
cout<<"YES\n";
continue;
}
sort(a+1,a+1+n);
int cnt = 0;
for (int i=1;i<=n;++i)if (!vis[i])
{
int cur = i;
int len = 0;
while (true)
{
vis[cur]=1;
++len;
cur = G[a[cur]];
if (vis[cur])break;
}
if (len%2==0)++cnt;
}
if (cnt&1)cout<<"NO\n";
else cout<<"YES\n";
}
}
E.Frequency Queries
题意:
给一颗n个节点的树,每个点的点权在111到nnn之间
有qqq次查询,每次查询格式如下:
v,l,kv,l,kv,l,k
要求返回:
从点vvv到根节点的最短路径上,出现次数大于lll次的点权中,按照出现此书排序的
第kkk多的点权值,如果有并列情况则返回任意即可。
分析:
我们可以离线解决这个问题
先记录出每个点上的查询。
然后从根节点开始dfsdfsdfs,在dfsdfsdfs的过程中维护线段树,利用线段树实现查询
每次dfsdfsdfs到一个新的节点时,我们线段树对这个点权出现次数加一
然后利用线段树查询这个点上所有的询问
之后回溯的过程时,我们再在线段树上删去这个点权
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
set<int>st[N],exi;
struct que{
int l,k,num;
};
vector<que>v[N];
int t,n,q,ans[N],val[N],tim[N];
struct edge{
int to,nex;
};
edge ed[N<<1];
int cnt,h[N];
void add(int st,int et){
cnt++;
ed[cnt].to=et;
ed[cnt].nex=h[st];
h[st]=cnt;
}
int sum[N<<2];
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void upd(int rt,int l,int r,int L,int R,int d)
{
if(L<=l && r<=R)
{
sum[rt]+=(r-l+1)*d;
return;
}
int mid=(l+r)>>1;
if(L<=mid) upd(rt<<1,l,mid,L,R,d);
if(R>mid) upd(rt<<1|1,mid+1,r,L,R,d);
pushup(rt);
}
int query(int rt,int l,int r,int L,int R)
{
if(L<=l && r<=R)
{
return sum[rt];
}
int mid=(l+r)>>1,res=0;
if(L<=mid) res += query(rt<<1,l,mid,L,R);
if(R>mid) res += query(rt<<1|1,mid+1,r,L,R);
return res;
}
int getval(int rt,int l,int r,int now){
if(l==r)return l;
int mid=(l+r)>>1;
if(sum[rt<<1]<now)return getval(rt<<1|1,mid+1,r,now-sum[rt<<1]);
else return getval(rt<<1,l,mid,now);
}
void dfs(int u){
if(st[tim[val[u]]].find(val[u])!=st[tim[val[u]]].end())st[tim[val[u]]].erase(val[u]);
upd(1,0,n,tim[val[u]],tim[val[u]],-1);
tim[val[u]]++;
st[tim[val[u]]].insert(val[u]);
upd(1,0,n,tim[val[u]],tim[val[u]],1);
for(int i=0;i<(int)v[u].size();i++){
int now=v[u][i].k,l=v[u][i].l,nu=v[u][i].num;
now+=query(1,0,n,0,l-1);
if(query(1,0,n,0,n)<now)ans[nu]=-1;
else ans[nu]=*st[getval(1,0,n,now)].begin();
}
for(int i=h[u];i;i=ed[i].nex)dfs(ed[i].to);
if(st[tim[val[u]]].find(val[u])!=st[tim[val[u]]].end())st[tim[val[u]]].erase(val[u]);
upd(1,0,n,tim[val[u]],tim[val[u]],-1);
tim[val[u]]--;
st[tim[val[u]]].insert(val[u]);
upd(1,0,n,tim[val[u]],tim[val[u]],1);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
cnt=0;
upd(1,0,n,0,0,n);
for(int i=1;i<=n;i++)scanf("%d",&val[i]),v[i].clear(),h[i]=0;
for(int i=2;i<=n;i++){
int x;
scanf("%d",&x);
add(x,i);
}
for(int i=1;i<=q;i++){
int x,l,k;
scanf("%d%d%d",&x,&l,&k);
v[x].push_back((que){l,k,i});
}
dfs(1);
for(int i=1;i<=q;i++){
printf("%d ",ans[i]);
}
puts("");
upd(1,0,n,0,0,-n);
}
return 0;
}
编程竞赛题目解析与算法实现
本文介绍了四道编程竞赛题目,包括A.FindArray、B.ArrayEversion、C.MinimizeDistance和D.YetAnotherSortingProblem。分别涉及花朵生长模拟、数组转置优化、货物分配最短路径和数组排序问题。通过详细分析和代码实现,展示了如何解决这些问题,并探讨了不同策略的选择和优化。
580

被折叠的 条评论
为什么被折叠?



