2020icpc上海(重温经典)

2020icpc上海(重温经典)

导语

涉及的知识点

思维,几何,搜索

链接:第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(上海)(重现赛)

题目

B

题目大意:一个n×m的地图,每个格子要么有地雷要么为一个数字,该数字代表相邻格子中地雷的个数(相邻的定义为有一个顶点公共,即最多有八个相邻),现在给出两个地图A,B,
(规模都为n×m)现在最多可以修改B中 ⌊ n m / 2 ⌋ \lfloor{nm/2}\rfloor nm/2个格子使得所有的数字之和与A相同,输出最后得到的B

思路:首先,题目一定有解,理由很简单,首先有个很简单的结论:A与A的反(即所有元素对应取反)的数字和相同,假设B到A需要X步,如果X小于等于 ⌊ n m / 2 ⌋ \lfloor{nm/2}\rfloor nm/2,那么直接从B变到A即可,否则就变到A的反

代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
string strs[1005];
string strs2[1005];
int a[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
int judge(int n,int m) {
    int ans=0;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            if(strs[i][j]!=strs2[i][j])
                ans++;
    return ans;
}
void print(int n,int m) {
    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++)
            printf("%c",strs[i][j]);
        printf("\n");
    }
}
void printfan(int n,int m) {
    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++)
            if(strs[i][j]=='.')
                printf("X");
            else
                printf(".");
        printf("\n");
    }
}
void solve() {
    int n,m;
    scanf("%d%d",&n,&m);
    int k=n*m/2;
    for(int i=0; i<n; i++)
        cin>>strs[i];
    for(int i=0; i<n; i++)
        cin>>strs2[i];
    int base=judge(n,m);
    if(base<=k)
        print(n,m);
    else
        printfan(n,m);
}
int main() {
    int t = 1;
    while(t--)
        solve();
    return 0;
}

D

题目大意:有一个数轴,范围为 [ 0 , n ] [0,n] [0,n],数轴上有两个人,位置和速度为 p 1 , v 1 , p 2 , v 2 p1,v1,p2,v2 p1,v1,p2,v2,求出两人路程和能够覆盖数轴的最小时间

思路:一共有四种情况(p1<p2)

  1. p1与p2相向而行
  2. p1走完全程
  3. p2走完全程
  4. p1与p2相背而行,最后走完p1-p2,或者先走完p1-p2,再走完p1和p2

前三种都可以容易的推出结论式,至于为什么不考虑同向而行,因为同向而行所需要的时间必然小于相向而行,并且与对应同向者走完全程的时间是相等的

对于第四种,需要考虑两人在哪里相遇,即考虑是先相遇还是后相遇,相遇的点必然在p1p2之间,在这个区间内二分求得相遇点即可

代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
int t;
double n,p1,v1,p2,v2,ans;
bool judge(double pos) {
    double t1=(min(pos-p1,p1)+pos)/v1,t2=(min(p2-pos,n-p2)+n-pos)/v2;
    ans=min(ans,max(t1,t2));//这里t1,t2的最小值是不满足条件的,但最大值是满足条件的
    return fabs(t1-t2)>1e-11&&t1<t2;
}
int main() {
    scanf("%d",&t);
    while(t--) {
        cin >>n>>p1>>v1>>p2>>v2;
        if(p1>p2&&fabs(p1-p2)>1e-11) {//确保p1为左
            swap(p1,p2);
            swap(v1,v2);
        }
        ans=1e10;//初始化答案为无穷大
        ans=min(ans,max((n-p1)/v1,p2/v2));//相向而行
        ans=min(ans,(min(n-p1,p1)+n)/v1);//p1走完
        ans=min(ans,(min(n-p2,p2)+n)/v2);//p2走完
        double l=p1,r=p2;//相对而行,二分交点
        for(int i=0; i<1000; i++) {//进行多次二分确保精度
            double mid=(l+r)/2;
            if(judge(mid))l=mid;
            else r=mid;
        }
        printf("%.10f\n",ans);
    }
    return 0;
}

G

题目大意:略

思路:比较简单,与其求有多少个x*y偶数,不如求出有多少个奇数对,直接减去奇数对的个数

代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;

void solve() {
    ll n;
    scanf("%lld",&n);
    ll ans=n*(n-1)/2;
    ll chu=n/3;
    ll yu=n%3;
    ll tmp=0;
    if(yu==0)
        tmp=chu*2-1;
    else if(yu==1)
        tmp=chu*2;
    else
        tmp=chu*2+1;
    ll kk=tmp*(tmp+1)/2;
    ans-=kk;
    printf("%lld\n",ans);
}
int main() {
    int t = 1;
    while(t--)
        solve();
    return 0;
}

H

题目大意:一个圆桌,有n个位置,等分分配,有k名客人坐在这n个位置上,有k盘菜分配在这n个位置上,为了使每个客人拿到一个菜,每次可以选择顺时针或逆时针旋转圆桌角度 2 π / n 2\pi/n 2π/n,客人遇到菜的时候要么拿走要么等下一个,求出能使每个客人拿到菜消耗的最小时间

思路:易证按照顺序来满足吃菜顺序可得最优解,具体证明可以画一个四个点的证一下或者参考文献

根据贪心的思路,将人与菜的位置进行排序,并且进行k次循环对应,判断当前顺序对应下得到的最优解是多少,对于当前顺序的求解,首先需要特判首尾两点,因为在实现首尾两点的对应关系的过程中,实际上已经实现了其余所有点的对应关系,之后再一 一判断当前点 i i i先顺时针满足 1 − i 1-i 1i再逆时针满足 i + 1 − k i+1-k i+1k得到的时间,最后求解出最小值即可

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e4+10;
int t,n,k,a[maxn],v[maxn],b[maxn];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        cin >>n>>k;
        int ans=0x3f3f3f3f;
        for(int i=0; i<k; i++)cin >>a[i];//存人的位置
        for(int i=0; i<k; i++)cin >>b[i];//存菜的位置
        sort(b,b+k);//按照标号升序排列
        sort(a,a+k);
        for(int j=0; j<k; j++) {//尝试k种对应方式
            for(int i=0; i<k; i++)
                v[i]=(a[(i+j)%k]-b[i]+n)%n;
            sort(v,v+k);
            ans=min(ans,min(v[k-1],n-v[0]));//直接满足最远的点,该解一定可行
            for(int i=1; i<k; i++)
                ans=min(ans,min(v[i-1],n-v[i])+v[i-1]+n-v[i]);
            /*
            以当前对应求解,先顺时针转,使得1-i的到达,再逆时针转
            使得i+1-k到达
            */
        }
        cout <<ans<<endl;
    }
    return 0;
}

I

题目大意:n个共圆心的圆,半径相差为1,m条线通过圆心将这n个圆平分,求所有交点之间的距离和

思路:

  1. 圆心到其余点的和,易得为 ∑ i = 1 n 2 m r \sum_{i=1}^n2mr i=1n2mr,无圆心的话这一部分就不用考虑了
  2. 对于这一层上的距离和,以某点 j j j为例,该点到达同一层的其余点 k k k需要的距离(除对称点外)都要考虑走弧还是走两个半径,即 m i n ( 2 r , a b s ( j − k ) × 2 π r / m ) min(2r,abs(j-k)×2\pi r/m) min(2r,abs(jk)×2πr/m),累和加上对称点的距离可得一个点到其余点的距离和为 2 π r × ( m − 1 ) + 2 m r 2\pi r×(m-1)+2mr 2πr×(m1)+2mr,多个圆可得 ∑ i = 1 n 2 π r × ( m − 1 ) + 2 m r \sum_{i=1}^n2\pi r×(m-1)+2mr i=1n2πr×(m1)+2mr
  3. 层与层之间的和可以由高层先到低层,再获得低层彼此之间的最小值即可,其余具体看代码

代码

#include<bits/stdc++.h>
using namespace std;
const double pi = acos(-1);
int n,m;
int main() {
    scanf("%d%d",&n,&m);
    double ans=0,point[1212],cir[1212],degree=pi/m;
    //最后结果,本层一个点到其余点距离和,本层某个点到本层和内部所有的距离和
    //m条线所成的角度
    ans+=(m>1)*n*(n+1)*m;//圆心到每个点的距离,大于1用来特判圆心
    for(int i=1; i<=n; i++) {//逐层进行
        for(int j=1; j<m; j++)//对于某点,假设为1,计算1到左半圆所有点的距离(除对称点)
            point[i]+=min(i*2.0,degree*j*i);//走弧还是半径
        point[i]*=2;//填补右半圆
        point[i]+=2*i;//加上对称点
        ans+=point[i]*m;//扩充到当前层每个点
        ans+=(cir[i-1]+(i-1)*2*m)*2*m;//计算本层圆通向内部的和,(i-1)*2*m是先前i-1层所有的点,2*m是本层的2*m个点
        cir[i]=cir[i-1]+(i-1)*2*m+point[i];//前缀和处理
    }
    printf("%.10f",ans);
    return 0;
}

M

题目大意:略

思路:题目的关键点是字符串的处理,需要设计一个虚拟的根节点,然后从根节点进行DFS获得每个节点的状况(是否需要保护)

代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e4+10;
int head[maxn],cnt,n,m;
bool vis[maxn],val[maxn];
struct node
{
    int to,next;
} e[maxn];
void Add(int to,int from)
{
    e[++cnt].to=to;
    e[cnt].next=head[from];
    head[from]=cnt;
}
bool DFS1(int u)
{
    bool flag=1,make=0;
    vis[u]=1;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v=e[i].to;
        if(vis[v])continue;
        flag&=DFS1(v);
        make=1;
    }
    if(!make)return val[u];
    return val[u]=flag;
}
int DFS2(int u)
{
    if(val[u]&&u!=1)return 1;
    vis[u]=1;
    int ans=0;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v=e[i].to;
        if(vis[v])continue;
        ans+=DFS2(v);
    }
    return ans;
}
void solve()
{
    unordered_map<string,int>mp;
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>n>>m;
    int id=1,pre=-1;
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(val,0,sizeof(val));
    cnt=0;
    for(int i=1; i<=n; i++)
    {
        string s,tmp="";
        cin >>s;
        int len=s.length();
        pre=1;
        for(int j=0; j<len; j++)
        {
            tmp+=s[j];
            if(s[j]=='/')
            {
                if(mp[tmp]==0)
                {
                    mp[tmp]=++id;
                    Add(pre,id);
                    Add(id,pre);
                }
                pre=mp[tmp];
            }
        }
        mp[s]=++id;
        val[id]=1;
        Add(pre,id);
        Add(id,pre);
    }
    for(int i=1; i<=m; i++)
    {
        string s,tmp="";
        cin >>s;
        int len=s.length();
        pre=1;
        for(int j=0; j<len; j++)
        {
            tmp+=s[j];
            if(s[j]=='/')
            {
                if(mp[tmp]==0)
                {
                    mp[tmp]=++id;
                    Add(pre,id);
                    Add(id,pre);
                }
                pre=mp[tmp];
            }
        }
        mp[s]=++id;
        Add(pre,id);
        Add(id,pre);
    }
    DFS1(1);
    memset(vis,0,sizeof(vis));
    cout <<DFS2(1)<<endl;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}

参考文献

  1. 2020ICPC(上海) - Walker(分类讨论+二分)
  2. 第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(上海)H Rice Arrangement —— 枚举,思维,有丶东西
  3. 第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(上海) > 题解讨论
  4. 2020上海ICPC H Rice Arrangement(乱搞,思维)
  5. 2020ICPC上海 H. Rice Arrangement
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值