2018-2019 ACM-ICPC, NEERC, Southern Subregional Contest, Qualification Stage

A. Coffee Break (CF gym 101911 A)

题目大意

\(n\)数字,对这\(n\)个数字分组,要求每组里任意两个数字的差值要大于\(d+1\),最小化组数,输出组数和每个数属于哪个组。

解题思路

贪心(对于一个数字\(a\),找最小的比\(a+d+1\)大的数和它一个组)是不对的,因为会存在说剩下最后几个都会被分到额外的组里,而采取其他方法可以避免此类情况。
这题和某年某地区的一个盖雪糕的题很像。

注意到组数对于是否可行具有单调性,我们可以二分组数,然后判断是否可行。设二分的组数为\(m\),则第\(1\)个,第\(1+m\)个,第\(1+2m\)个......都在同一组,我们只要判断同组的数中的差值是否大于\(d+1\)即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

bool check(int x,vector<pair<int,int>> &t,vector<int> &qwq,int d){
    for(size_t i=0;i<t.size();++i){
        qwq[t[i].second]=i%x;
        if ((int)i>=x) if (t[i].first-t[i-x].first-1<d) return false;
    }
    return true;
}

int main(void) {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n,m,d;
    cin>>n>>m>>d;
    vector<pair<int,int>> t(n);
    for(int i=0;i<n;++i){
        cin>>t[i].first;
        t[i].second=i;
    }
    sort(t.begin(),t.end());
    vector<int> qwq(n);
    vector<int> ans(n);
    int cnt=1;
    int l=1,r=n+1;
    while(l<r){
        int mid=(l+r)>>1;
        if (check(mid,t,qwq,d)) r=mid,cnt=mid,ans=qwq;
        else l=mid+1;
    }
    cout<<cnt<<endl;
    for(int i=0;i<n;++i) cout<<ans[i]+1<<(i==n-1?'\n':' ');
    
    return 0;
}


B. Glider (CF gym 101911 B)

题目大意

坐标系上,一个点在\(y=h\)的直线上,它可以选择一个地方开始,它每往\(x\)轴正方向前进一个单位,它就会往\(y\)轴负方向减少一个单位。坐标轴上存在一些特殊的区域(用x_l,x_r表示),\(x\)坐标在特殊的区域的时候,它每往\(x\)轴正方向前进一个单位,\(y\)坐标不会变。现在需要你选择一个点出发,使得它落到\(x\)轴时,\(x\)方向的位移最大。

解题思路

可容易看出从某个特殊区域的起点出发是最优的,于是我们枚举从哪个特殊区域出发,初始高度有多高,这个点就能在非特殊区域前进多少。于是我们预处理特殊区域长度的前缀和和特殊区域之间非特殊区域之间长度的前缀和,对于从某一个特殊区域出发,二分找到第一个大于高度\(h\)的非特殊区域的长度和,其包含的特殊区域的长度加上高度就是从这个区域出发到落到\(x\)轴的水平位移。取最大值即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int main(void) {
    int n,h;
    read(n);
    read(h);
    LL sum[n+1]={0};
    LL dis[n+1]={0};
    int lr=0;
    for(int l,r,i=1;i<=n;++i){
        read(l);
        read(r);
        sum[i]=sum[i-1]+(LL)(r-l);
        if (i==1) lr=l;
        dis[i]=dis[i-1]+l-lr;
        lr=r;
    }
    LL ans=0;
    for(int i=1;i<=n;++i){
        int it=lower_bound(dis+i+1,dis+n+1,dis[i]+h)-dis;
        ans=max(sum[it-1]-sum[i-1]+h,ans);
    }
    write(ans,'\n');
    return 0;
}



C. Bacteria (CF gym 101911 C)

题目大意

队友写的

解题思路

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LD;
 
const int maxn=2e5+10;
priority_queue <LL> que;
 
int main()
{
    int n;scanf("%d",&n);
    LL Max=0;
    for (int i=1;i<=n;i++)
    {
        LL x;scanf("%lld",&x);
        que.push(-x);
        Max=max(x,Max);
    }
    int ans=0;
    while (que.size()!=1)
    {
        LL now=que.top();
        que.pop();
        if (now==que.top()) {que.pop();ans--;}
        que.push(now*2);
        if (now*2>Max*2) {puts("-1");return 0;}
        ans++;
    }
    if (ans>n) puts("-1");else printf("%d\n",ans);
    return 0;
}



D. Disposable Switches (CF gym 101911 D)

题目大意

队友写的。

解题思路

神奇的代码
#include <bits/stdc++.h>
using namespace std;
const long long maxn = 0x3f3f3f3f;
typedef long long LL;

LL n;
LL a[200020], b[200020], c[200020];
map <LL, LL> d;
vector <LL> f[200020];
LL cnt;

bool Work() {
    LL i, j, k;
    for (i = 1; i <= cnt; ++ i) {
        k = 0;
        LL x = c[f[i][1]];
        LL nn = sqrt(x);
        for (j = 1; j <= nn; ++ j) {
            if (x % j == 0) {
                ++ k;
                a[f[i][k]] = j;
                b[f[i][k]] = x / j;
                if (k == f[i][0]) break;
                if (j == x / j) continue;
                ++ k;
                a[f[i][k]] = x / j;
                b[f[i][k]] = j;
                if (k == f[i][0]) break;
            }
        }
        if (k < f[i][0]) return 0;
    }
    return 1;
}

int main() {
	
    LL i, j, k;
	scanf("%I64d", &n);
    cnt = 0;
    for (i = 1; i <= n; ++ i) {
        scanf("%I64d", &c[i]);
        if (d[c[i]]) {
            f[d[c[i]]].push_back(i);
            ++ f[d[c[i]]][0];
        }
        else {
            d[c[i]] = ++ cnt;
            f[cnt].push_back(1);
            f[cnt].push_back(i);
        }
    }
    if (Work()) {
        printf("YES\n");
        for (i = 1; i <= n; ++ i) {
            printf("%I64d %I64d\n", a[i], b[i]);
        }
    }
    else {
        printf("NO\n");
    }
	
	return 0;
}


E. Painting the Fence (CF gym 101911 E)

题目大意

\(n\)个数,\(m\)条指令,每个指令包含一个数\(b_i\),意味着将这\(n\)个数中,最先出现\(b_i\)和最晚出现\(b_i\)的两个位置之间的所有数都变成\(b_i\)。问\(m\)条指令后各个数分别是多少。如果数\(b_i\)出现的次数小于两次,则不做任何操作,直接跳到下一条指令。

解题思路

注意到当对某个区间进行同化后,这个区间要不就保持同化后的样子,要不就再继续被整个同化,不存在这个区间从中间某处同化成另一个数。

既然如此,我们维护每个数的位置数组,当对某个区间同化时,我们就把这个区间的所有数对应的位置信息都消去,遇到已经同化过的区间,我们直接跳过即可(之前同化的时候已经消去了),然后我们就可以通过这个跳过数组得知这个区间的颜色都是相同的,且是起跳点的那个位置的颜色。最坏情况下就每个数都被处理过一次。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

const int N=3e5+8;

set<int> col[N];

int main(void) {
    int n;
    read(n);
    int a[n+1]={0};
    for(int i=1;i<=n;++i){
        read(a[i]);
        col[a[i]].insert(i);
    }
    int m;
    read(m);
    int jump[n+1]={0};
    int c;
    while(m--){
        read(c);
        if (col[c].size()<2) continue;
        int l=*col[c].begin();
        int r=*col[c].rbegin();
        for(int i=l;i<=r;) {
            if (jump[i]) i=jump[i];
            if (i>r) break;
            col[a[i]].erase(i);
            ++i;
        }
        jump[l]=r+1;
    }
    for(int i=1;i<=n;){
        printf("%d%c",a[i],i==n?'\n':' ');
        if (jump[i]){
            int qwq=a[i];
            int en=jump[i];
            ++i;
            while(i<en){
                printf("%d%c",qwq,i==n?'\n':' ');
                ++i;
            }
        }else ++i;
    }
    return 0;
}


F. Tickets (CF gym 101911 F)

题目大意

队友写的

解题思路

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LD;

const int maxn=2e5+10;
int sub[100];
struct node
{
    int num,id,res;
}a[maxn];

bool cmp(node a,node b) {return a.num<b.num;}
bool bac(node a,node b) {return a.id<b.id;}

int main()
{
    int n;scanf("%d",&n);
    for (int i=1;i<=n;i++) {scanf("%d",&a[i].num);a[i].id=i;}
    sort(a+1,a+1+n,cmp);

    int t=1;
    for (int i=0;i<1000000;i++)
    {
        int k=i,L=0,R=0;
        for (int j=0;j<3;j++) {R+=k%10;k/=10;}
        for (int j=0;j<3;j++) {L+=k%10;k/=10;}
        sub[abs(L-R)]++;
        while (a[t].num==i && t<=n)
        {
            k=a[t].num;L=0;R=0;
            for (int j=0;j<3;j++) {R+=k%10;k/=10;}
            for (int j=0;j<3;j++) {L+=k%10;k/=10;}
            int ans=0;
            for (int j=0;j<abs(L-R);j++) ans+=sub[j];
            a[t].res=ans;
            t++;
        }
    }

    sort(a+1,a+1+n,bac);
    for (int i=1;i<=n;i++) printf("%d\n",a[i].res);

    return 0;
}


G. Tree Reconstruction (CF gym 101911 G)

题目大意

一棵树,\(n\)个点标号\(1\)\(n\)。对于每一条边,删去它,树被分成两部分,此时会有两个数字,这两个数字分别是这两部分中标号的最大值。现给出对于每一条边,这两个的最大值,要求你给出一种可能的树,使得满足上述要求。不存在输出\(NO\)

解题思路

首先标号\(n\)一定出现\(n-1\)次,否则就是\(NO\)

然后对于某个点对\((u,v)\),如果它仅出现一次,那么一定是\((u,v)\)之间连边,因为其他边的话就非\(u\)\(v\)了。

如果某个点对\((u,v)\)出现多次,比如\(cnt\)次,那么\((u,v)\)之间一定还通过了其他点连接,且其数量一定是\(cnt-1\)个点,而且这些点一定比\(u,v\)小,并且不出现在点对里。如果没有符合条件的点,则不存在这棵树。

如此即可构造出一棵符合条件的树。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int main(void) {
    int n;
    read(n);
    set<int,greater<int>> qwq;
    for(int i=1;i<=n;++i)  qwq.insert(i);
    map<pair<int,int>,int,greater<pair<int,int>>> cnt;
    int qaq=0;
    for(int u,v,i=1;i<n;++i){
        read(u);
        read(v);
        if (u<v) swap(u,v);
        if (u==n) ++qaq;
        cnt[make_pair(u,v)]++;
        qwq.erase(u);
        qwq.erase(v);
    }
    bool flag=true;
    if (qaq!=n-1) flag=false;
    vector<int> edge[n+1];
    for(auto i:cnt){
        if (!flag) break;
        if (i.second==1) edge[i.first.first].push_back(i.first.second),edge[i.first.second].push_back(i.first.first);
        else{
            auto st=i.first.first;
            while(i.second>1){
                if (qwq.size()==0) flag=false;
                if (*qwq.begin()>i.first.second) flag=false;
                if (!flag) break;
                edge[st].push_back(*qwq.begin());
                edge[*qwq.begin()].push_back(st);
                st=*qwq.begin();
                qwq.erase(qwq.begin());
                i.second--;
            }
            edge[st].push_back(i.first.second);
            edge[i.first.second].push_back(st);
        }
    }
    if (!flag) puts("NO");
    else{
        puts("YES");
        for(int i=1;i<=n;++i)
            for(auto j:edge[i]){
                if (j<i) continue;
                printf("%d %d\n",i,j);
            }
    }
    return 0;
}


H. Theater Square (CF gym 101911 H)

题目大意

队友写的

解题思路

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LD;

const int maxn=200+10;

int main()
{
    int n,m;scanf("%d%d",&n,&m);
    int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    int now1=n-(x2-x1+1),now2=x2-x1+1;
    int ans=0;
    if (m%2!=0) ans+=now1;
    if ((y1-1)%2!=0) ans+=now2;
    if ((m-y2)%2!=0) ans+=now2;
    printf("%d\n",(ans+1)/2);
    
    return 0;
}


I. Heist (CF gym 101911 I)

题目大意

原本有一些标号连续且递增的键帽在一夜之间被偷了一部分,只剩下一些键帽,要求你求出最小的可能被偷的键帽数量。

解题思路

排序俩俩作差即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main(void) {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n;
    cin>>n;
    vector<int> qwq;
    int u;
    while(n--){
        cin>>u;
        qwq.push_back(u);
    }
    sort(qwq.begin(),qwq.end());
    LL ans=0;
    for(size_t i=1;i<qwq.size();++i)
        ans+=qwq[i]-qwq[i-1]-1;
    cout<<ans<<endl;
    return 0;
}


J. Buying a TV Set (CF gym 101911 J)

题目大意

给定电视尺寸的长宽的最大值和长宽比,问你有多少种电视尺寸在不超过长宽最大值的前提下满足长宽比。

解题思路

分别作商取最小值就可以了。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main(void) {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    LL a,b,x,y;
    cin>>a>>b>>x>>y;
    LL qwq=__gcd(x,y);
    x/=qwq;
    y/=qwq;
    LL ans=min(a/x,b/y);
    cout<<ans<<endl;
    return 0;
}


K. Medians and Partition (CF gym 101911 K)

题目大意

队友写的。

解题思路

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LD;

const int maxn=5e3+10;
int a[maxn];

int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+1+n);

    int ans=0;
    for (int i=1;i<=n;i++)
        if (a[i]>=m && (i-1)*2<n) {ans=n-2*(i-1);break;}
    printf("%d\n",ans);

    return 0;
}


L. Ray in the tube (CF gym 101911 L)

题目大意

上下两面镜子,光反射,上下两面镜子的某些位置有监测点,要求你从上下两面镜子的某面镜子的某处朝某个方向射一道光线,使得射到监测点的数量尽可能多。光线射到镜子上的时候的坐标必须是整数。

解题思路

我们考虑光线从一面镜子到另一面镜子,\(x\)方向的位移\(d\),如果\(d\)可以分解成\(d=2^{i}*m\)\(m\)是个奇数,那么可以用位移为\(2^{i}\)\(m\)次来替代,这样子不仅可以保证位移是\(d\)时候到达的点,位移\(2^{n}\)都可以到达,且到达的点更多更优,所以我们只要考虑位移为\(2^{i}\)的情况即可。\(x\)坐标在\(2^{i}\)同余下的余数相同的这些点都可以走到,注意另一面镜子的监测器的坐标有一个\(2^{i}\)的平移。

注意位移为\(0\)的情况。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main(void) {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n,y1;
    cin>>n>>y1;
    int a[n+1]={0};
    map<int,int> cnt;
    for(int i=1;i<=n;++i) {cin>>a[i]; cnt[a[i]]++;}
    int m,y2;
    cin>>m>>y2;
    int b[m+1]={0};
    for(int i=1;i<=m;++i) {cin>>b[i]; cnt[b[i]]++;}
    int ans=0;
    for(auto i:cnt) ans=max(ans,i.second);
    for(int s=1;s<=(int)1e9;s<<=1){
        map<LL,int> qwq;
        LL qaq=(s<<1);
        for(int i=1;i<=n;++i) qwq[a[i]%qaq]++;
        for(int i=1;i<=m;++i) qwq[(b[i]+s)%qaq]++;
        for(auto i:qwq) ans=max(ans,i.second);
    }
    cout<<ans<<endl;
    return 0;
}


posted @ 2020-03-14 18:33  ~Lanly~  阅读(296)  评论(0编辑  收藏  举报