2020牛客暑期多校训练营(第三场)D、E、F

Points Construction Problem

解法:

首先判断无解的情况:

1.易得m为奇数时无解

2.大于最大组数4*n时无解

3.小于最小组数时无解  :

接下来构造合法情况:

采用不断从最小组合情况里从上到下的顺序不断拆出,放到遥远处,使得组合数为m

每次拆一个正方形组合数加num

当该正方形的四个方向有2个正方形相邻时,num=4;

当该正方形的四个方向只有1个正方形相邻时,num=2;

当出现还差2个组合数时,需要特殊处理:

假设现在轮到( i , j )位置正方形拆开时,若增加的num=2 ,则放到遥远处

若num=4 ,则会多加2个组合,可以构造放到(1,1)位置的左边,就可以时增加的num降至2

即可

#include<bits/stdc++.h>
using namespace std;
int t;
int n,m;
int aa,bb;
int getmin(int a)//最小组合数 
{
    int ans=1e9;
    for(int i=1;i<=a;i++)
    {
        int j=(int)ceil((double)a/i);
        if(i+j<=ans&&i&&j&&abs(i-j)<=1)
        {
            aa=i;
            bb=j;
            if(aa>bb) swap(aa,bb);
        }
        ans=min(ans,i+j);
    }
    return 2*ans;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        if(m%2||m>4*n||getmin(n)>m)//无解 
        {
            printf("No\n");
        }
        else
        {
            int mi=getmin(n);//最小组合数 
            vector<pair<int,int> > v;
            vector<pair<int,int> > u;
            int num=n;
            for(int i=1;i<=aa&&num;i++)
            {
                for(int j=1;j<=bb&&num;j++)
                {
                    v.push_back(make_pair(i,j));//按最小的方式构造 再拆开 
                    num--;
                }
            }
            int now=n-1;
            int flag=1;
            while(m - mi >= 4&&now>=0)
            {
                int x=v[now].first;
                int y=v[now].second;
                if(y==1||x==1)
                {
                	mi+=2;
				}
				else mi+=4;
                u.push_back(make_pair(flag,100000000));//放到遥远处 
                now--;
                flag+=2;
                if(mi==m) break;
            }
            if(m-mi==2)
            {
				int x=v[now].first;
                int y=v[now].second;
                if(y==1||x==1)
                {
                	u.push_back(make_pair(flag,100000000));//放到遥远处
				}
				else
				{
					u.push_back(make_pair(0,1));//放到(1,1)旁边 
				}
				now--;
            }
            puts("Yes");
            for(int i=0;i<v.size()-u.size();i++)
            {
                auto T=v[i];
                printf("%d %d\n",T.first,T.second);
            }
            for(auto T:u)
            {
                printf("%d %d\n",T.first,T.second);
            }
        }

    }
}

Fraction Construction Problem

解法:

 

情况1:先考虑能不能通过构造出c,d,e,f  我们可以构造出一左边分数减去右边分数就是a/b,例如:\frac{a+1}{b}-\frac{1}{b}=\frac{a}{b}

但是由于存在限制条件d,f<b ,此需要约分变化一下,若gcd(a,b)!=1 ,则b=b/gcd,就满足该限制了

A=a/gcd 、B=b/gcd\frac{A+1}{B}-\frac{1}{B}=\frac{A}{B}  此时B<b了

情况2:当不满足情况1时,说明一定gcd(a,b)==1 ,若b==1或b为素数(相异质因数),那么无解 -1 -1 -1 -1

情况3:当d*f=b,且d,f互质 通分得:

再拓展gcd即可

#include<bits/stdc++.h>
#define ll long long
using namespace std;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
    if(!b)
    {
        d=a;
        x=1;
        y=0;
    }
    else
    {
        exgcd(b,a%b,d,y,x);   
        y-=x*(a/b);
    }
}
   
ll mod_count(ll a,ll c,ll b)//求解模线性方程ax=c(mod b)
{
    ll x,y;
    ll gcd;
    exgcd(a,b,gcd,x,y);
    ll aa=a;
    ll bb=b;
    while(x<=0||y>=0)//注意x,y 符号 
    {
        x+=bb;
        y-=aa;
    }
    x=(x*(c/gcd));//次题gcd一定为1 
    y=(y*(c/gcd));
    printf("%lld %lld %lld %lld\n",x,bb,-1*y,aa);
    return x;
}
  
const int N=2e6+5;
int mark[N];
int prim[N];
int cnt;
void initial()
{
    cnt=0;
    for (int i=2 ; i<N ; ++i)
    {
        if (mark[i]==0)
            prim[cnt++]=i;
        for (ll j=1ll*i*i;j<N;j+=i)
        {
            if(mark[j]==0) mark[j]=i;
            else mark[j]=min(mark[j],i);          
        }
    }
}
int main()
{
    initial();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll A,B;
        scanf("%lld%lld",&A,&B);
        ll gcd=__gcd(A,B);
        if(gcd!=1)//先后!!!
        {
            A/=gcd;
            B/=gcd;
            printf("%lld %lld %lld %lld\n",A+1,B,1ll,B);
            continue;
        }
        if(B==1||!mark[B])
        {
            printf("-1 -1 -1 -1\n");
            continue;
        }
        int flag=0;
        ll d,f;
        d=mark[B];
        f=B/d;        
        while(f%mark[B]==0)
        {
            d*=mark[B];
            f/=mark[B];
        }
        if(f==1)
        {
            printf("-1 -1 -1 -1\n");
            continue;
        }
//      printf("f=%lld !",f);
        mod_count(f,A,d);
    }
}

Operating on a Graph

#include<bits/stdc++.h>
using namespace std;
int fa[800005];
int color[800005];
list<int> L[800005];
vector<int> G[800005];
int find(int x)
{
    if(fa[x]==x) return fa[x];
    else return fa[x]=find(fa[x]);
}
int main()
{
    int t;    
    scanf("%d",&t);


    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++)
        {
            fa[i]=i;
            L[i].clear();
            G[i].clear();
            L[i].push_back(i);
        }
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v) ;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        int Q;

        scanf("%d",&Q);
        while(Q--)
        {
            int x;
            scanf("%d",&x);
            if(fa[x]!=x) continue;
            //int fx=find(x);
            int cnt=L[x].size();
            while(cnt--)
            {
                int y=L[x].front();
                L[x].pop_front();
                for(auto T:G[y])
                {
                    int fT=find(T);
                    if(fT!=x)
                    {
                        fa[fT]=x;
                        L[x].splice(L[x].end(),L[fT]);
                    }
                }
            }
        }
        for(int i=0;i<n;i++)
        {
            printf("%d ",find(i));
        }
        puts("");


    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值