2018, XI Samara Regional Intercollegiate Programming Contest 解题报告

2018, XI Samara Regional Intercollegiate Programming Contest

传送门


A. Restoring Numbers

题意:

给出两个数 a a a b b b 的和 s s s 和最大公因数 g g g,要你求出任意一组 a a a b b b 的解。

题解:

我们可以知道 g c d ( a , b ) = g gcd(a,b)=g gcd(a,b)=g,则我们假设 a = p ∗ g a=p*g a=pg b = q ∗ g b=q*g b=qg,那么 s = ( p + q ) ∗ g s=(p+q)*g s=(p+q)g,由此可得有解的第一个条件是 s % g = 0 s\%g=0 s%g=0

至于为什么是第一个,是因为题目描述 a a a b b b 是正数,所以上设的 p p p q q q 要大于 0 0 0,所以有解的第二个条件是 s / g > = 2 s/g>=2 s/g>=2,此时我们令 p = 1 p=1 p=1 q = s / g − 1 q=s/g-1 q=s/g1,就得到了 p p p q q q 的一组解,同时就得到了 a a a b b b 的一组解。

代码:

#include 
#include 
#include 
using namespace std;

int main(){
     
    int s,g;scanf("%d%d",&s,&g);
    if(s%g!=0||s/g==1) printf("-1\n");
    else printf("%d %d\n",g,s-g);
    return 0;
}

B. Minimal Area

题意:

按顺时针方向给出一个凸包上的所有点,问你以这个凸包上的点为顶点的非退化三角形的最小面积(的两倍,这个两倍应该是为了方便计算)。

题解:

考虑凸包的任意一条边作为底边,底边相同时高最小面积最小,而能做到高最小的顶点即为离底边最近的点,即从凸包上取相邻三点一定能找到最小的三角形,所以我们每次顺时针取三个点计算面积取最小值。

代码:

#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn=200005;
struct vec{
     
    ll x,y;
}p[maxn];
vec create(vec a,vec b){
     
    return vec{
     b.x-a.x,b.y-a.y};
}
ll area(vec a,vec b){
     
    ll res=(a.x*b.y)-(b.x*a.y);
    return res<0?-res:res;
}
int main(){
     
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld%lld",&p[i].x,&p[i].y);
    p[n+1]=p[1];p[n+2]=p[2];
    ll ans=0x7fffffffffffffff;
    for(int i=1;i<=n;i++){
     
        vec a=create(p[i],p[i+1]);
        vec b=create(p[i],p[i+2]);
        ans=min(ans,area(a,b));
    }
    printf("%lld\n",ans);
    return 0;
}

C. Third-Party Software

题意:

给出 n n n 条线段,问你最少选择多少个点,使得每条线段至少包含一个点,并给出这些点。

题解:

对线段进行排序,记录当前存放的节点,初始为 0 0 0。当遍历到的线段不包含当前点时,把这条线段的右端点放进答案,并更新当前点。这样首先能保证每条线段至少覆盖一个点,其次由于每次贪心选择右端点,保证选择的点尽可能少。

代码:

#include 
#include 
#include 
using namespace std;
const int maxn=200005;
struct node{
     
    int l,r;
    bool operator<(const node &t)const{
     
        if(r!=t.r) return r<t.r;
        else return l<t.l;
    }
}p[maxn];
int ans[maxn],tot;
int main(){
     
    int n;scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d%d",&p[i].l,&p[i].r);
    sort(p,p+n);
    int now=p[0].r;
    ans[tot++]=now;
    for(int i=1;i<n;i++){
     
        if(p[i].l>now){
     
            now=p[i].r;
            ans[tot++]=now;
        }
    }
    printf("%d\n",tot);
    for(int i=0;i<tot;i++) printf("%d%c",ans[i],(i==tot-1)?'\n':' ');
    return 0;
}

D. Transfer Window

题意:

总共有 n n n 类球员,你手里有 k k k 个种类为 a 1 a_1 a1 a 2 a_2 a2,…, a k a_k ak 的球员,你想通过和另一个队伍进行一系列交换最终获得 k k k 个种类为 b 1 b_1 b1 b 2 b_2 b2,…, b k b_k bk 的球员。有一个 n ∗ n n*n nn 的矩阵,每个有序对 ( x , y ) (x,y) (x,y) 的值为 0 0 0 1 1 1,若为 1 1 1 则表示你可以通过手中有的 x x x 交换得到 y y y。一次交换后你失去 x x x 并获得 y y y,另一个队伍失去 y y y 并获得 x x x。问能否找到一个交换方式满足条件,如果有还要输出交换次数和交换内容,不需要控制交换次数最少。

思路:

n n n k k k 只有 300 300 300 的数据范围,可以想到一个网络流的做法来求是否有解,但是没有考虑好如何输出交换方案。

题解:

未通过


E. Substring Reverse

题意:

给出两个串 s s s t t t,问是否可以通过恰好一次如下操作使 t t t s s s 相同:

t t t 的某个子串反转

题解:

首先子串当然可以是单个字符。

然后由于恰好一次,所以我们只需要找到两个串第一个字符不相同的位置和最后一个字符不相同的位置,判断把中间整个部分反转能否实现即可。

代码:

#include 
#include 
#include 
using namespace std;
const int maxn=200005;
char s[maxn],t[maxn];
int main(){
     
    scanf("%s%s",s+1,t+1);
    int len=(int)strlen(s+1);
    bool flag=true;
    int st=maxn,ed=-1;
    for(int i=1;i<=len;i++){
     
        if(s[i]!=t[i]){
     
            st=min(st,i);
            ed=max(ed,i);
        }
    }
    if(ed!=-1){
     
        for(int i=st,j=ed;i<=ed;i++,j--){
     
            if(s[i]!=t[j]){
     
                flag=false;
                break;
            }
        }
    }
    if(flag) printf("YES\n");
    else printf("NO\n");
    return 0;
}

F. Tree Restoration

题意:

告诉你每个点的所有后代,问你是否存在这样一棵树,如果存在输出每条树边。

题解:

正向存边,进行拓扑排序,每次检查被遍历到的点和当前点的父节点的祖先是否相同,并每次更新父节点。最后检查找到的树是否满足初始的边的关系。

代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef pair<int,int> pii;
const int maxn=1005;
int ind[maxn],fa[maxn];
int mp[maxn][maxn];
vector<int> vec[maxn];
int main(){
     
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) fa[i]=-1;
    for(int i=1;i<=n;i++){
     
        int c;scanf("%d",&c);
        for(int j=0;j<c;j++){
     
            int x;scanf("%d",&x);
            vec[i].push_back(x);
            mp[i][x]=1;
            ind[x]++;
        }
    }
    queue<int> q;
    int rt=-1;
    for(int i=1;i<=n;i++){
     
        if(!ind[i]){
     
            q.push(i);
            fa[i]=i;
            rt=i;
        }
    }
    if((int)q.size()!=1){
     
        printf("NO\n");
        return 0;
    }
    int cnt=1;
    while(!q.empty()){
     
        int now=q.front();q.pop();
        int sz=(int)vec[now].size();
        for(int i=0;i<sz;i++){
     
            int to=vec[now][i];
            if(fa[to]!=fa[now]&&fa[to]!=-1){
     
                printf("NO\n");
                return 0;
            }
            fa[to]=now;
            ind[to]--;
            if(!ind[to]){
     
                q.push(to);
                cnt++;
            }
        }
    }
    if(cnt!=n){
     
        printf("NO\n");
        return 0;
    }
    for(int i=1;i<=n;i++){
     
        if(i==rt) continue;
        for(int j=0,sz=(int)vec[i].size();j<sz;j++){
     
            int to=vec[i][j];
            if(!mp[fa[i]][to]){
     
                printf("NO\n");
                return 0;
            }
        }
    }
    printf("YES\n");
    for(int i=1;i<=n;i++){
     
        if(i!=rt) printf("%d %d\n",fa[i],i);
    }
    return 0;
}

G. Underpalindromity

题意:

给出 n n n 个数,对于每个长度为 k k k 的子区间,你可以花费 1 1 1 来使得某个数临时(即无后效性)增大 1 1 1,使得这个长度为 k k k 的子区间变为一个回文。问每个长度为 k k k 的子区间都做完的最小花费。

题解:

未通过。


H. Safe Path

题意:

给出一个 n ∗ m n*m nm 的点阵,其中 S S S 为起点, F F F 为终点,所有 M M M 周围 k k k 步以内都不能走,问从起点出发能否走到终点。

题解:

先进行一次 b f s bfs bfs 预处理,把所有不能走的点都打上标记,然后再进行一次 b f s bfs bfs 走迷宫即可。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=200005;
int n,m,d,sx,sy;
char s[maxn];
vector<char> mp[maxn];
vector<int> vec[maxn];
struct node{
     
    int x,y,ans;
};
vector<node> v;
int dir[4][2]={
     1,0,-1,0,0,1,0,-1};
bool check(int x,int y){
     
    return x>=0&&x<n&&y>=0&&y<m&&!vec[x][y];
}
void overflow(){
     
    queue<node> q;
    for(int i=0,sz=(int)v.size();i<sz;i++){
     
        vec[v[i].x][v[i].y]=1;
        q.push(v[i]);
    }
    while(!q.empty()){
     
        node now=q.front();q.pop();
        int x=now.x,y=now.y,tmp=now.ans;
        if(tmp==0) continue;
        for(int i=0;i<4;i++){
     
            int xx=x+dir[i][0];
            int yy=y+dir[i][1];
            if(check(xx,yy)){
     
                vec[xx][yy]=1;
                q.push(node{
     xx,yy,tmp-1});
            }
        }
    }
}
int bfs(int x,int y){
     
    if(!check(x,y)) return -1;
    vec[x][y]=1;
    queue<node> q;
    q.push(node{
     x,y,0});
    while(!q.empty()){
     
        node now=q.front();q.pop();
        int xx=now.x,yy=now.y;
        if(mp[xx][yy]=='F') return now.ans;
        for(int i=0;i<4;i++){
     
            int xxx=xx+dir[i][0];
            int yyy=yy+dir[i][1];
            if(check(xxx,yyy)){
     
                vec[xxx][yyy]=1;
                q.push(node{
     xxx,yyy,now.ans+1});
            }
        }
    }
    return -1;
}
int main(){
     
    scanf("%d%d%d",&n,&m,&d);
    for(int i=0;i<n;i++){
     
        mp[i].resize(m);
        vec[i].resize(m);
        scanf("%s",s);
        for(int j=0;j<m;j++){
     
            mp[i][j]=s[j];
            if(mp[i][j]=='M'){
     
                vec[i].push_back(1);
                v.push_back(node{
     i,j,d});
            }else{
     
                vec[i].push_back(0);
            }
            if(mp[i][j]=='S') sx=i,sy=j;
        }
    }
    overflow();
    printf("%d\n",bfs(sx,sy));
    return 0;
}

I. Guess the Tree

题意:

交互题。

初始给出一个 n n n,表示有一颗 n n n 个点的完全二叉树,所以 n = 2 h − 1 n=2^h-1 n=2h1。现在你要用 2.5 ∗ h ∗ n 2.5*h*n 2.5hn 次以内的询问求出每个点的父节点。每次询问你可以知道两个点 x x x y y y 之间的树上距离。

题解:

分析可得红色矩阵应当螺旋移动才能使得步数最少,而给定矩阵边长相同,在纸上模拟直接找到规律即可。

代码:

未通过


J. Parallelograms

题意:

给出 n n n 条木棍,问最多能得到多少平行四边形。

题解:

先用桶存好每个长度的边的数量,再把每条边的数量扔进优先队列,优先取数量多的,每次拿走两根,拿两次意味着多了一个平行四边形。

写题解的时候突然想到不用优先队列,好像直接算就好了。

代码:

#include 
#include 
#include 
#include 
using namespace std;
const int maxn=200005;
int cnt[maxn];
priority_queue<int> pq;
int main(){
     
    int n;scanf("%d",&n);
    for(int i=0;i<n;i++){
     
        int x;scanf("%d",&x);
        cnt[x]++;
    }
    for(int i=1;i<maxn;i++){
     
        if(cnt[i]) pq.push(cnt[i]);
    }
    int ans=0;
    while(true){
     
        if(pq.empty()) break;
        int tmp1=pq.top();pq.pop();
        if(tmp1<2) break;
        tmp1-=2;
        if(tmp1>=2) pq.push(tmp1);
        if(pq.empty()) break;
        int tmp2=pq.top();pq.pop();
        if(tmp2<2) break;
        tmp2-=2;
        if(tmp2>=2) pq.push(tmp2);
        ans++;
    }
    printf("%d\n",ans);
    return 0;
}

K. Video Reviews

题意:

n n n 个人,你需要获得至少 m m m 个人的支持。一个人会支持你可能有两种条件:

  • 你收买了他。
  • 在他(第 i i i 个人)之前有 a i a_i ai 个人已经支持你了。

问你做少需要收买多少人。

题解:

二分答案。 c h e c k check check 时直接遍历数组检查是否足够 m m m 人。

代码:

#include 
#include 
#include 
using namespace std;
const int maxn=200005;
int a[maxn];
int main(){
     
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int l=0,r=n,ans=n;
    while(l<=r){
     
        int mid=(l+r)/2;
        int tmp=0,cnt=0;
        for(int i=1;i<=n;i++){
     
            if(cnt>=a[i]) cnt++;
            else if(tmp<mid) cnt++,tmp++;
        }
        if(cnt>=m){
     
            ans=mid;
            r=mid-1;
        }else{
     
            l=mid+1;
        }
    }
    printf("%d\n",ans);
    return 0;
}

L. Queries on a String

题意:

给出一个串 s s s,进行 q q q 次操作,每次操作可以选择向一个初始为空的串 t t t 的末尾丢进一个字符,每次操作结束后回答当前串 t t t 是否时串 s s s 的一个子序列。

题解:

序列自动机。每次丢进一个字符后在序列自动机中找到对应的下一个位置,判断是否能找到,能找到说明可行。

代码:

#include 
#include 
#include 
using namespace std;
const int maxn=200005;
int nxt[maxn][30],pos[30];
char s[maxn],op[10],ch[5];
int tmp[maxn];
int main(){
     
    scanf("%s",s+1);
    int len=(int)strlen(s+1);
    for(int i=0;i<26;i++) pos[i]=-1;
    for(int i=len;i>=0;i--){
     
        for(int j=0;j<26;j++){
     
            nxt[i][j]=pos[j];
        }
        pos[s[i]-'a']=i;
    }
    memset(tmp,-1,sizeof(tmp));
    tmp[0]=0;
    int now=0;
    int q;scanf("%d",&q);
    while(q--){
     
        scanf("%s",op);
        if(op[1]=='u'){
     
            scanf("%s",ch);
            if(tmp[now]==-1||nxt[tmp[now]][ch[0]-'a']==-1) printf("NO\n");
            else{
     
                printf("YES\n");
                tmp[now+1]=nxt[tmp[now]][ch[0]-'a'];
            }
            now++;
        }else{
     
            tmp[now--]=-1;
            if(tmp[now]==-1) printf("NO\n");
            else printf("YES\n");
        }
    }
    return 0;
}

M. Forgotten Spell

题意:

给出三个串 a a a b b b c c c。问每个串至多改变一个字符能否使得三个串相等。

题解:

分类讨论。

首先找出三个串中有几个位置三个字符全都不相等,即 c n t 1 cnt1 cnt1,然后找出三个串中有多少位置三个字符全都相等,即 c n t 2 cnt2 cnt2

  • c n t 1 > = 2 cnt1>=2 cnt1>=2 则必定无解。

  • c n t 1 = 1 cnt1=1 cnt1=1

    • c n t 2 = l e n − 1 cnt2=len-1 cnt2=len1 (即只有一个位置全都不相等,其他位置全都相等),则多解(可以将不相等的字符都换成相同的任意字符)。

    • c n t 2 = l e n − 2 cnt2=len-2 cnt2=len2(即有一个位置全都不相等,一个位置两个字符与另一个不相等,其他位置全都相等),此时需要确定哪个位置上某个字符与另两个不同,得到唯一解。如:

      ai
      bi
      cj
      

      则此时我们需要把第三个串的 j j j 变为 i i i,把前两个串的 a a a b b b 都变为 c c c。结果为:

      ci
      ci
      ci
      
    • 其他情况无解。

  • c n t 2 = 0 cnt2=0 cnt2=0

    把每个两个字符相同且与第三个字符不相同的位置记录下来,存放不同的串是第几个串以及不同的位置。

    • 若这样的位置超过三个则必定无解(需要超过三次修改)。

    • 若这样的位置恰好三个,则考虑为以下两种情况中的哪一种:

      aab || aaa
      aca || abb
      daa || caa
      
      • 第一种是可行的,可以变成以下情况:

        aaa
        aaa
        aaa
        
      • 第二种不行,因为第二种对第二个串的操作次数超过了一次。

    • 若这样的位置恰好两个,则考虑为以下两种情况的哪一种:

      aa || aa
      aa || ab
      cc || ca
      
      • 第一种有多解,可以变成以下两种的任意一种:

        ac || ca
        ac || ca
        ac || ca
        
      • 第二种有唯一解,可以变成以下情况:

        aa
        aa
        aa
        

代码:

#include 
#include 
#include 
using namespace std;
typedef pair<int,int> pii;
const int maxn=200005;
char s[3][maxn];
pii p[maxn];
int tot;
void solve(){
     
    int len=(int)strlen(s[0]);
    int cnt1=0,cnt2=0;
    for(int i=0;i<len;i++){
     
        if(s[0][i]!=s[1][i]&&s[0][i]!=s[2][i]&&s[1][i]!=s[2][i]){
     
            cnt1++;
        }
    }
    for(int i=0;i<len;i++){
     
        if(s[0][i]==s[1][i]&&s[0][i]==s[2][i]&&s[1][i]==s[2][i]){
     
            cnt2++;
        }
    }
    if(cnt1>=2){
     
        printf("Impossible");
    }else if(cnt1==1){
     
        if(cnt2==len-1) printf("Ambiguous");
        else if(cnt2==len-2){
     
            int a=-1,b=-1,c=-1;
            for(int i=0;i<len;i++){
     
                if(s[0][i]!=s[1][i]&&s[0][i]!=s[2][i]&&s[1][i]==s[2][i]){
     
                    a=0;b=1;c=2;
                    break;
                }
                if(s[1][i]!=s[0][i]&&s[1][i]!=s[2][i]&&s[0][i]==s[2][i]){
     
                    a=1;b=0;c=2;
                    break;
                }
                if(s[2][i]!=s[0][i]&&s[2][i]!=s[1][i]&&s[0][i]==s[1][i]){
     
                    a=2;b=0;c=1;
                    break;
                }
            }
            for(int i=0;i<len;i++){
     
                if(s[a][i]!=s[b][i]&&s[a][i]!=s[c][i]){
     
                    if(s[b][i]!=s[c][i]) printf("%c",s[a][i]);
                    else printf("%c",s[b][i]);
                }else{
     
                    printf("%c",s[a][i]);
                }
            }
        }else printf("Impossible");
    }else{
     
        for(int i=0;i<len;i++){
     
            if(s[0][i]!=s[1][i]&&s[0][i]!=s[2][i]&&s[1][i]==s[2][i]){
     
                p[tot++]=pii(0,i);
            }
            if(s[1][i]!=s[0][i]&&s[1][i]!=s[2][i]&&s[0][i]==s[2][i]){
     
                p[tot++]=pii(1,i);
            }
            if(s[2][i]!=s[0][i]&&s[2][i]!=s[1][i]&&s[0][i]==s[1][i]){
     
                p[tot++]=pii(2,i);
            }
        }
        if(tot>3) printf("Impossible");
        else if(tot==3){
     
            if(p[0].first==p[1].first||p[0].first==p[2].first||p[1].first==p[2].first) printf("Impossible");
            else{
     
                for(int i=0;i<len;i++){
     
                    if(s[0][i]==s[1][i]) printf("%c",s[0][i]);
                    else if(s[0][i]==s[2][i]) printf("%c",s[0][i]);
                    else printf("%c",s[1][i]);
                }
            }
        }else if(tot==2){
     
            if(p[0].first==p[1].first) printf("Ambiguous");
            else{
     
                for(int i=0;i<len;i++){
     
                    if(s[0][i]==s[1][i]) printf("%c",s[0][i]);
                    else if(s[0][i]==s[2][i]) printf("%c",s[0][i]);
                    else printf("%c",s[1][i]);
                }
            }
        }else{
     
            printf("Ambiguous");
        }
    }
    printf("\n");
}
int main(){
     
    for(int i=0;i<3;i++) scanf("%s",s[i]);
    solve();
    return 0;
}

你可能感兴趣的:(cf,XI,Samara,Regional,Intercollegiate,Gym-101755)