Gym.102059: 2018-2019 XIX Open Cup, Grand Prix of Korea(寒假gym自训第一场)

整体来说,这一场的质量比较高,但是题意也有些难懂。

E.Electronic Circuit

题意:  给你N个点,M根线,问它是否是一个合法的电路。

思路:  一个合法的电路,经过一些串联并联关系,最后有一个点是正极,一个是负极,我们就来模拟这个串联的过程即可,并联的关系会因为我们使用了set而抵消,所以可以不去管它。  模拟串联: 我们选择一个度数为2的点,删去它与两边的点u,v的连线,然后连接u-v,知道不存在度数为2的点。 最后当有两个点度数为1,而且不存在度数为大于大于2的点,则合法。

(选择set其实比较巧妙,因为不用考虑并联。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=100010;
set<int>S[maxn]; queue<int>q;
int main()
{
    int N,M,u,x,y;
    scanf("%d%d",&N,&M);
    rep(i,1,M){
        scanf("%d%d",&x,&y);
        S[x].insert(y); S[y].insert(x);
    }
    rep(i,1,N) if(S[i].size()==2) q.push(i);
    while(!q.empty()){
        u=q.front(); q.pop();
        if(S[u].size()!=2) continue;
        x=*S[u].begin(); S[u].erase(S[u].begin());
        y=*S[u].begin(); S[u].erase(S[u].begin());
        S[x].erase(u); S[y].erase(u);
        S[x].insert(y); S[y].insert(x);
        if(S[x].size()==2) q.push(x);
        if(S[y].size()==2) q.push(y);
    }
    int cnt=0,ok=1;
    rep(i,1,N) {
        if(S[i].size()==1) cnt++;
        if(S[i].size()>1) ok=0;
    }
    if(ok&&cnt==2) puts("Yes");
    else puts("No");
    return 0;
}
View Code

 

F .Fake Plastic Trees

题意:让你建造一棵树,使得它有N个叶子节点,而且需要满足对于每个节点,它的左子树大小等于右子树大小,或者左子树大小比右边大1。

每次你构造一棵树,你可以选择之前构造过的树作为它的儿子。  现在让你构造不超过125棵树,满足上诉条件。

输出的方式是,输出V,表示构造的树的多少。 接下来V上,每行表示左子树的标号和右子树的编号。 最后输出根节点的标号。

(读了好久才懂了题意,主要是输出这里,我们构造的东西每次都是重复利用了之前的子树的。

思路:每次除2构造即可。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=100010;
int x[maxn],y[maxn],tot; map<ll,int>mp;
void get(ll num)
{
    if(mp.find(num)!=mp.end()) return ;
    if(num==1LL) {
        x[++tot]=-1; y[tot]=-1; mp[num]=tot;
        return ;
    }
    ll mid=num/2;
    get(num-mid); get(mid);
    tot++;  x[tot]=mp[num-mid]; y[tot]=mp[mid];
    mp[num]=tot;
}
int main()
{
    int T; ll N;
    scanf("%d",&T);
    while(T--){
        scanf("%lld",&N);
        tot=-1; mp.clear(); get(N);
        printf("%d\n",tot+1);
        rep(i,0,tot) printf("%d %d\n",x[i],y[i]);
        printf("%d\n",tot);
    }
    return 0;
}
View Code

 

H .Fractions

题意:求多少对(x,y),满足A<=x<=B ; C<=y<=D,而且(x+y)/gcd(x,y)<1000;

思路:水题,我们枚举化简后的x'=x/gcd; y'=y/gcd;然后看有多少gcd可以在给定区间即可。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int main()
{
    ll A,B,C,D,ans=0;
    scanf("%lld%lld%lld%lld",&A,&B,&C,&D);
    rep(i,1,999)
     rep(j,1,999-i){
       if((__gcd(i,j))==1){
          //A<=ix<=B c<=jx<=D
          ll tmp=min(D/j,B/i)-max((A-1)/i,(C-1)/j);
          if(tmp>0) ans+=tmp;
       }
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

I .Game on Plane

题意:给定N个点,围成一个圈,每次玩家选择两个点连线,不得与之前连的线相交。 如果玩家连线形成了一个多边形或者没有选的点,输。

思路:sg函数,每次连线会把大圈分为两个小圈,跑sg即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=5010;
int sg[maxn],vis[maxn];
void init()
{
    sg[1]=0; sg[2]=1;
    rep(i,3,5000){
        rep(j,0,i-2){
            vis[sg[j]^sg[i-2-j]]=i;
        }
        rep(j,0,5000) {
            if(vis[j]!=i) { sg[i]=j; break;}
        }
    }
}
int main()
{
    init();
    int T,N; scanf("%d",&T);
    while(T--){
        scanf("%d",&N);
        if(!sg[N]) puts("Second");
        else puts("First");
    }
    return 0;
}
View Code

 

L .Timsort

题意:给定长度为N的数组a[],Q次询问,每次给出长度L,让你按照规定跑。 每次从当前位置出发,一直跑不降序列,或者下降序列。跑完后,如果大于等于L,从下一个位置继续开始,否则要凑齐长度L,同时累计凑数的个数。

思路:预处理,每次按照规定跑即可,因为我们可以记忆化,所以我们可以假设每次的问题是不一样的,最坏的情况下是N/1+N/2+N/3...+N/N~=NlogN,所以就ok了,想不到这一点,这个水题就跑掉了。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep2(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int maxn=100010;
int a[maxn],R[maxn][2],ans[maxn][2];
int main()
{
    int N,Q,x;
    scanf("%d",&N);
    rep(i,1,N) scanf("%d",&a[i]);
    rep2(i,N,1) {
        R[i][0]=R[i][1]=1;
        if(i+1<=N&&a[i]<=a[i+1]) R[i][0]=R[i+1][0]+1;
        if(i+1<=N&&a[i]>a[i+1]) R[i][1]=R[i+1][1]+1;
    }
    scanf("%d",&Q);
    while(Q--){
        scanf("%d",&x);
        if(ans[x][0]) printf("%d %d\n",ans[x][0],ans[x][1]);
        else {
            int A=0,B=0;
            rep(i,1,N){
                A++; if(i==N) break;
                int t=R[i][a[i]>a[i+1]];
                if(t<x){
                    if(i+x-1<=N) B+=x-t;
                    else B+=N-(i+t-1);
                    i=i+x-1;
                }
                else i=i+t-1;
            }
            ans[x][0]=A; ans[x][1]=B;
            printf("%d %d\n",A,B);
        }
    }
    return 0;
}
View Code

 

posted @ 2019-01-30 19:18  nimphy  阅读(918)  评论(0编辑  收藏