2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest
2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest
Solved:
C、Berpizza
D、Firecrackers
E、Four Segments
F、Full Turn
H、K and Medians
J、Road Reform
K、The Robot
N、Waste Sorting
C、Berpizza
两个优先队列模拟,一个优先队列以编号(进入顺序)为关键字,另一个以权值为关键字,用一个vis数组标记某个编号是否已经出去,模拟即可。
struct node1{
int id;
int cost;
bool operator < (node1 a) const {
return id>a.id;
}
};
struct node2{
int id;
int cost;
bool operator < (node2 a) const {
if(cost==a.cost){
return id>a.id;
}else{
return cost<a.cost;
}
}
};
priority_queue<node1>q1;
priority_queue<node2>q2;
int vis[500005];
int main()
{
int T,now=0;
cin>>T;
while(T--){
int pos;
scanf("%d",&pos);
if(pos==1){
++now;
node1 x;
node2 y;
int cost;
scanf("%d",&cost);
x.id=now;
x.cost=cost;
y.id=now;
y.cost=cost;
q1.push(x);
q2.push(y);
}else if(pos==2){
int nowpos;
while(1){
nowpos=q1.top().id;
q1.pop();
if(!vis[nowpos]){
vis[nowpos]=1;
break;
}
}
printf("%d ",nowpos);
}else if(pos==3){
int nowpos;
while(1){
nowpos=q2.top().id;
q2.pop();
if(!vis[nowpos]){
vis[nowpos]=1;
break;
}
}
printf("%d ",nowpos);
}
}
return 0;
}
D、Firecrackers
最多可以放置的爆竹个数必定是警察到小偷的距离-1。为了在被抓之前看到爆竹爆炸,一定是选择最小的几个放置。首先选择爆炸时间前k个,k=最多可以放置的爆竹个数,最后求出在小偷不被抓的情况下,每次放爆竹时尽量选择大的且可以在规定时间内爆炸的即可。
int s[200010];
int ss[200010];
int main()
{
int T;
scanf("%d",&T);
while(T--){
mem(s,0);
ll n,m,a,b;
scanf("%lld%lld%lld%lld",&n,&m,&a,&b);
for(int i=1;i<=m;i++)
{
scanf("%d",&s[i]);
}
sort(s+1,s+m+1);
int xx = min(abs(b - a)- 1,m);
if(a<b)
{
for(int i=1;i<=xx;i++)
{
ss[i] = b - 1 - s[i] - 1;
}
sort(ss+1,ss+1+xx);
int ans = 0;
int q = 0;
for(int i=1;i<=xx;i++)
{
if(ans<=ss[i])
{
ans++;
q++;
}
}
cout<<q<<endl;
}
else{
a = n-a+1;
b = n-b+1;
for(int i=1;i<=xx;i++)
{
ss[i] = b - 1 - s[i] - 1;
}
sort(ss+1,ss+1+xx);
int ans = 0;
int q = 0;
for(int i=1;i<=xx;i++)
{
if(ans<=ss[i])
{
ans++;
q++;
}
}
cout<<q<<endl;
}
}
}
E. Four Segments
第一小的和第三小的相乘即是本题答案。
int main()
{
int T;
cin>>T;
while(T--){
int a[6];
for(int i=1;i<=4;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+5);
printf("%d\n",a[1]*a[3]);
}
}
F. Full Turn
题意:给你2n对点,每组两对点,表示一个人在点(x1,y1) 处且注视着(x2,y2)处,当每个人同时按相同方向旋转360度,问有多少对人可以相互注视到。
思路:把每个人和其注视点化成一个向量,此时可以发现,当两个人可以注视到时,两个向量方向相反。即可以表示为 (ax,xy)=(-bx,-by)。因为此时只关注方向而忽略大小,因此通过求gcd可以把每个向量化到最简,然后用map存储每个方向拥有的向量个数,最后统计相反方向的向量个数即可。
map<pair<ll,ll>,int>mp;
vector<pair<ll,ll> >v;
int main()
{
int T;
scanf("%d",&T);
while(T--){
v.clear();
mp.clear();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
ll x1,y1,x2,y2;
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
ll x,y;
x=x2-x1;
y=y2-y1;
ll num=__gcd(abs(x),abs(y));
x=x/num;
y=y/num;
if(mp[make_pair(x,y)]>0){
mp[make_pair(x,y)]++;
}else{
mp[make_pair(x,y)]=1;
v.push_back(make_pair(x,y));
}
}
ll sum=0;
for(int i=0;i<v.size();i++){
ll nowx=v[i].first,nowy=v[i].second;
//cout<<nowx<<" "<<nowy<<"xx"<<endl;
sum+=1ll*mp[make_pair(nowx,nowy)]*mp[make_pair(-nowx,-nowy)];
}
sum=sum/2;
printf("%lld\n",sum);
}
}
H. K and Medians
题意:给你1~n的数组a,再给你数组p,每次你可以在a数组中选择 k个数字(k一定是奇数),删掉这k个数字除了中位数外的数字,问能否将a数组变成p数组。
思路:首先将所有要删除的数筛出来。这些数需要满足,1、总数%(k-1)==0,接下来看,我们需要2、有一个不能删除的点作为中位数的点,然后去删除那些需要删除的数,最后要考虑这个点的位置,3、点的位置后需要删除的数不能少于k/2个。
int vis[200005];
int a[200005];
void solve()
{
int n,k,m;
mem(vis,0);
scanf("%d%d%d",&n,&k,&m);
for(int i=1;i<=m;i++){
int num;
scanf("%d",&num);
vis[num]=1;
}
int pos=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
a[++pos]=i;
}
}
for(int i=k/2+1;i<=pos;i++){
//cout<<pos-i<<endl;
if(pos%(k-1)==0&&pos-i+1>=k/2&&a[i]!=a[i-1]+1){
cout<<"YES"<<endl;
return ;
}
}
cout<<"NO"<<endl;
return ;
}
int main()
{
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
J. Road Reform
题意:给出一幅图,通过删边把这幅图变成一棵生成树,要让这颗树上最大边的权值变成k,每步操作可以把某一条边的权值+1或-1,问你最少需要操作几次。
思路:1、首先考虑所有比k小的边就可以构成一棵树,那么只需要找到最接近k的边,加入这棵树,然后让其他小于k的边去构成树。2、如果比k小的边只能连接一部分点,那么我们的目的就是让比k的边尽量少且小,那么考虑最小生成树,即把由比k小的边连接的所有点当作一个大点,其他点独立,通过最小生成树算法去跳出那些比k大的边即可。
struct node{
ll u,v,w;
}s[200005],ss[200005];
bool cmp(node a,node b){
return a.w<b.w;
}
ll fa[200005];
ll vis[200005];
ll find(ll x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(ll x,ll y)
{
x=find(x);
y=find(y);
fa[x]=y;
}
int main()
{
ll T,n,m,k;
cin>>T;
while(T--){
memset(vis,0,sizeof vis);
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<=m;i++){
ll u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
s[i].u=u;
s[i].v=v;
s[i].w=w;
}
sort(s+1,s+m+1,cmp);
int rpos=m;
for(int i=1;i<=m;i++){
if(s[i].w<=k){
merge(s[i].u,s[i].v);
}else{
rpos=i-1;
break;
}
}
int kk=1;
for(int i=1;i<n;i++){
if(find(i)!=find(i+1)){
kk=0;
break;
}
}
if(kk){
if(rpos+1<=m){
printf("%lld\n",min(abs(s[rpos].w-k),abs(s[rpos+1].w-k)));
}else{
printf("%lld\n",abs(s[rpos].w-k));
}
}else{
int now=0;
ll sum=0;
for(int i=1;i<=m;i++){
if(s[i].w>k){
ss[++now].u=s[i].u;
ss[now].v=s[i].v;
ss[now].w=s[i].w-k;
}
}
sort(ss+1,ss+now+1,cmp);
for(int i=1;i<=now;i++){
int ufa=find(ss[i].u),vfa=find(ss[i].v);
if(ufa!=vfa){
merge(ss[i].u,ss[i].v);
sum+=ss[i].w;
}
}
printf("%lld\n",sum);
}
}
}
K. The Robot
发现障碍必定是放在机器人要经过的路上,通过求机器人的路径,对每个点暴力搜索即可。
string s;
map<pair<int,int>,int>vis;
bool bfs(int x,int y,int step)
{
int nowx = x;
int nowy = y;
if(s[step]=='L')
{
nowx++;
}
else if(s[step]=='R')
{
nowx--;
}
else if(s[step]=='U')
{
nowy--;
}
else{
nowy++;
}
for(int i=step+1;i<s.length();i++)
{
if(s[i]=='U')
{
nowy++;
}
else if(s[i]=='D')
{
nowy--;
}
else if(s[i]=='R')
{
nowx++;
}
else{
nowx--;
}
if(nowx==x&&nowy==y)
{
if(s[i]=='L')
{
nowx++;
}
else if(s[i]=='R')
{
nowx--;
}
else if(s[i]=='U')
{
nowy--;
}
else{
nowy++;
}
}
}
//cout<<nowx<<" "<<nowy<<endl;
if(nowx==0&&nowy==0)
{
return true;
}
else {
return false;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
bool flag = 0;
vis.clear();
vis[make_pair(0,0)] = 1;
cin>>s;
int xx = 0;
int yy = 0;
for(int i=0;i<s.length();i++)
{
if(s[i]=='U')
{
yy++;
}
else if(s[i]=='D')
{
yy--;
}
else if(s[i]=='R')
{
xx++;
}
else{
xx--;
}
//cout<<xx<<yy<<endl;
if(!vis[make_pair(xx,yy)])
{
if(bfs(xx,yy,i))
{
printf("%d %d\n",xx,yy);
flag = 1;
break;
}
vis[make_pair(xx,yy)] = 1;
}
}
if(!flag)
{
puts("0 0");
}
}
}
N. Waste Sorting
阅读理解。
string s;
int main()
{
int T;
cin>>T;
while(T--){
int c1,c2,c3;
int a1,a2,a3,a4,a5;
scanf("%d%d%d %d%d%d%d%d",&c1,&c2,&c3,&a1,&a2,&a3,&a4,&a5);
if(c1<a1||c2<a2)
{
puts("NO");
continue;
}
c1=c1-a1-a4;
c2=c2-a2-a5;
if(c1<0&&c2<0)
{
if(c3>=abs(c1+c2)+a3) puts("YES");
else puts("NO");
}
else if(c1>=0&&c2>=0)
{
if(c3>=a3) puts("YES");
else puts("NO");
}
else if(c1<=0&&c2>=0)
{
if(c3>=abs(c1)+a3) puts("YES");
else puts("NO");
}
else if(c1>=0&&c2<=0)
{
if(c3>=abs(c2)+a3) puts("YES");
else puts("NO");
}
else
{
puts("NO");
}
}
}