Codeforces Round #624 (Div. 3)
第一次unrated的div3 然而做了一个多小时只做出了A也是迷
B
题意:n个1~100的数字 给定m个位置 这m个位置可以与后面一个元素交换位置 进行任意次操作 能否使数组不递减
脑补了一个对于不可以交换的位置如果后面有比它小的元素就NO否则就YES的假算法
5 2
1 3 5 2 4
2 3
由这个点可以看出应该是前面所有数的最大值都必须比后面所有数要小
正解应为模拟冒泡排序过程 能换&&需要换就换 最后检查是否有序 复杂度为n方
或者考虑每一段连续的可以交换的位置 它们一定是能变成有序的 那么用双指针遍历这一段 然后再对这一段进行排序 复杂度为nlogn
还看到了别人的神仙On做法 人都傻了 对于每个断点 如果前面的最大值比当前位置要小就No否则就Yes 看似和我的假算法的区别在我是拿当前位置去和后面每个数比较 正解是拿前一段的最大值去与后面的每个数比较 正确性由此保证
bool fuck(int n)
{
for(int i=1;i<n;i++)
{
if(a[i]>a[i+1])
{
return false;
}
}
return true;
}
bool Che(int n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<n;j++)
{
if(a[j+1]<a[j]&&vis[j])
{
swap(a[j+1],a[j]);
}
}
}
return fuck(n);
}
bool Che(int n,int x)
{
int j;
for(int i=1;i<=n;i++)
{
j=i;
while(vis[j])j++;
sort(a+i,a+j+1);
i=j;
}
return fuck(n);
}
bool Che(int n,int x,int y)
{
int _max=a[1];//到目前为止所有数的最大值
int __ma=0;//这一段除外 前面的若干段中的最大值
for(int i=2;i<=n;i++)
{
if(!vis[i-1])
{
__ma=_max;
}
_max=max(_max,a[i]);
if(__ma>a[i])
return false;
}
return true;
}
C
题意:长度为n的小写字母串 m次操作 2e5 2e5 每次操作给个数字x 代表遍历到位置x结束 最后再从头到尾遍历一遍 问整个过程每个字母出现的次数
维护26个字母的前缀和 然后每次更新即可 然而
while(T--)
{
// memset(sum,0,sizeof(sum));
// memset(ans,0,sizeof(ans));
cin>>n>>m;
cin>>s;
for(int j=0;j<26;j++)
{
ans[j]=0;
sum[j][0]=0;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<26;j++)
{
sum[j][i+1]=sum[j][i]+(s[i]-'a'==j);
}
}
while(m--)
{
cin>>x;
for(int j=0;j<26;j++)
{
ans[j]+=sum[j][x];
}
}
x=n;
for(int j=0;j<26;j++)
{
ans[j]+=sum[j][x];
}
for(int j=0;j<26;j++)
{
cout<<ans[j]<<" ";
}
cout<<"\n";
}
注释的memset是TLE的原因 其实并不需要将整个数组置0
题解为利用差分的思想 更新每个位置计算的次数 再用前缀和的思想将其还原为真正的次数 一次扫描即可
或者将操作排序后对字符串的每个位置二分查找会计算当前位置的操作数量 nlogn
void slove1(int n,int m,string s)
{
int to;
int ret=0;
vector<int>p;
p.resize(m);
for(int i=0;i<m;i++)
cin>>p[i];
sort(p.begin(),p.end());
for(int i=0;i<n;i++)
{
to=s[i]-'a';
ans[to]++;
ans[to]+=m-(lower_bound(p.begin(),p.end(),i+1)-p.begin());
}
}
int cnt[N];
void slove2(int n,int m,string s)
{
int ret=0;
int x,to;
vector<int>p;
p.resize(m);
for(int i=0;i<m;i++)
{
cin>>x;
x--;
cnt[x]++;
}
for(int i=n-1;i>0;i--)
{
cnt[i-1]+=cnt[i];
}
for(int i=0;i<n;i++)
{
to=s[i]-'a';
ans[to]+=cnt[i]+1;
cnt[i]=0;
}
}
D
a<=b<=c(1e4) 自增或者自减 得到ABC B%A==0 C%B==0 求最少操作次数和ABC
set<int>s[N];
int main()
{
int T,a,b,c,ans,ret,A,B,C;
set<int>::iterator it1,it2;
for(int i=1;i<N;i++)
{
for(int j=1;j*j<=i;j++)
{
if(i%j==0)
{
s[i].insert(j);
s[i].insert(i/j);
}
}
}
cin>>T;
while(T--)
{
ans=0x7fffffff;
ret=0;
cin>>a>>b>>c;
for(int i=1;i<2*c+1;i++)
{
for(it1=s[i].begin();it1!=s[i].end();++it1)
{
for(it2=s[*it1].begin();it2!=s[*it1].end();++it2)
{
ret=abs(i-c)+abs((*it1)-b)+abs((*it2)-a);
if(ans>ret)
{
C=i;
B=*it1;
A=*it2;
ans=ret;
}
}
}
}
cout<<ans<<endl;
cout<<A<<" "<<B<<" "<<C<<endl;
}
return 0;
}
E
题意:给定二叉树的结点数量和所有结点的深度和 第一个结点为根节点,深度为0 输出2~n结点的父节点
从链开始构造 从最下面开始拿 拿走之后如果还是多了就放到最上面去 如果少了就放到应该放的地方去 如果应该放的地方放不了就放到最上面去 这种考码力的大模拟真的不应该写不动的啊 sigh...
#include <bits/stdc++.h>
#define show(x) cout<<#x<<" "<<x<<endl
using namespace std;
const int N=5005;
int father[N],layer[N],deepth[N];
vector<int>v[N];//深度为i的结点的数组
int main()
{
int T,n,d,lb,ub,tt,now;
cin>>T;
v[1].push_back(1);
v[1].push_back(1);
while(T--)
{
cin>>n>>d;
ub=n*(n-1)/2;
lb=0;
tt=0;
for(int i=1;;i++)
{
tt+=1<<(i-1);
lb+=(1<<(i-1))*(i-1);
if(tt>=n)
{
lb-=(i-1)*(tt-n);
break;
}
}
if(d<lb||d>ub)
{
cout<<"NO"<<endl;
continue;
}
cout<<"YES"<<endl;
layer[1]=1;
for(int i=2;i<=n;i++)
{
v[i].clear();
father[i]=i-1;//第i个节点的父亲节点是i-1
layer[i]=1;//第i层的结点个数是1
deepth[i]=i-1;//第i个节点的深度是i-1
}
now=ub;
for(int i=n;i>=2;i--)
{
// show(now);
if(now==d)break;
now-=deepth[i];
if(now>=d)//放到最上面去
{
for(int j=2;j<i;j++)
{
if(layer[j]<layer[j-1]*2)
{
layer[j]++;
deepth[i]=j-1;
now+=j-1;
break;
}
}
}
else//如果能放合适的地方就放 不能的话就放合适的地方的下面
{
int to=d-now;
if(layer[to+1]<layer[to]*2)
{
deepth[i]=to;
now+=to;
}
else
{
for(int j=2;j<i;j++)
{
if(layer[j]<layer[j-1]*2)
{
layer[j]++;
deepth[i]=j-1;
now+=j-1;
break;
}
}
}
}
}
for(int i=1;i<=n;i++)
{
v[deepth[i]].push_back(i);
v[deepth[i]].push_back(i);
}
for(int i=2;i<=n;i++)
{
father[i]=v[deepth[i]-1].back();
v[deepth[i]-1].pop_back();
}
for(int i=2;i<=n;i++)
cout<<father[i]<<" ";
cout<<endl;
}
return 0;
}
F
题意:n个点 2e5 给定每个点的初始位置和带方向的前进速度 求每个点对在整个运动过程中的最短距离之和
关键在提取出 符合xi<xj vi>vj条件的点对一定相遇这一性质 以及二维关系上先将一维排序再对另一维进行处理的常规手法 以及 每次计算对答案的贡献以便计算前缀和的这一抽象的方法 树状数组都快不会用了 本题待重做
while(cin>>n)
{
pr.resize(n);
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
mp.clear();
for(ll i=0;i<n;i++)
{
cin>>x[i];
}
for(ll i=0;i<n;i++)
{
cin>>v[i];
pr[i]=make_pair(x[i],v[i]);
}
sort(v,v+n);
cnt=unique(v,v+n)-v;
for(ll i=0;i<cnt;i++)
{
mp[v[i]]=i+1;
}
sort(pr.begin(),pr.end());
for(ll i=0;i<n;i++)
{
x[i]=pr[i].first;
v[i]=mp[pr[i].second];
}
ans=0;
for(ll i=0;i<n;i++)
{
//点数*这一点的位置-这些点的和
ans+=sum(c1,v[i])*x[i]-sum(c2,v[i]);
update(c1,v[i],1);
update(c2,v[i],x[i]);
}
cout<<ans<<endl;
}