2020牛客多校第3场:[Points Construction Problem + 思维题+构造]


题目链接


题目大意:就是给你n个边长为1的正方形,要求用这些正方形拼成周长为m的图形,并输出这些正方形的坐标,如果没有输出No


首先如果这些正方形都零散分布那么周长就是 4 ∗ n 4*n 4n,如果将这些正方形都聚集在一起尽量拼成正(长)方形的周长最小 2 ∗ ( l + r ) 2*(l+r) 2(l+r):比如: 7 = 3 ∗ 3 , 8 = 3 ∗ 3 , 9 = 3 ∗ 3 , 10 = 3 ∗ 4 , 11 = 3 ∗ 4 ( l 和 r 要 尽 可 能 相 进 ) 7=3*3,8=3*3,9=3*3,10=3*4,11=3*4(l和r要尽可能相进) 7=338=339=3310=3411=34(lr)这里求错了一直wa


下面就是如何构造:我们知道假设所有的正方形都是零散的那么总周长是4*n,假如你将两个正方向合并成链状那么总周长会减少2,如过合并成矩形状就是在两个正方形相夹得对角地方总周长会减少4

我们假设有18个正方形那么最小的组合就是用黑色框起来部分那么我们先把涂黑部分的框先构造出来如果再往里面填正方形的时候就是减4了,如果想要减2就向下面角继续延申填就好了


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define hash Hash
#define next Next
#define count Count
#define pb push_back
#define f first
#define s second
using namespace std;
const int N = 2e6+10, mod = 1e9 + 7;
const long double eps = 1e-5;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args)
{
    read(first);
    read(args...);
}
 
PII  ans[55];
int main()
{
    int T;
    read(T);
    while(T --)
    {
        int n, m;
        int cnt = 0;
        read(n,m);
        int l = 0, r = 0;
        while(r * r <= n) r ++;
        if((r - 1) * (r - 1) == n) r = r - 1, l = r;
        else if((r - 1) * r >= n) l = r - 1;
        else l = r;
        // cout << l <<" " << r << endl;
        if(m & 1 || m < (l + r) * 2 || m > 4 * n) puts("No");
        else
        {
            puts("Yes");
            int tmp = 4 * n - m;//tmp是你要减少的边数
            int x = 1, y = 1;
            //构造边缘:特判tmp==0的时候
            for(int k = 0, i = 1, j = 0; cnt < l + r - 1; ++ k)
            {
                if(!tmp) break;
                if(k & 1) ans[cnt ++] = {(x + i),y}, i ++;
                else ans[cnt ++] = {y,(x + j)}, j ++;
                 tmp -= 2 * (cnt > 1);
            }
            if(!tmp)
            {
                for(int i = 0; i < cnt; ++ i)
                printf("%d %d\n",ans[i].f,ans[i].s);
                //剩下的按照对角的规律输出就好:这里输出的坐标不连续就好
                for(int i = cnt; i < n; ++ i)
                printf("%d %d\n",x  + n + i,y  + n + i);
            }
            else
            {
                while(tmp)
                {
                    int f = 0;  y ++;
                    for(int i = 0; tmp >= 4; ++ i)//贪心的先往里面填-4
                    {//控制填充的层数
                        if(f % (r - 1) == 0 && f != 0) y ++, f = 0;
                        ans[cnt ++] = {x + f + 1,y};
                        f ++;
                        tmp -= 4;
                    }
                     
                    if(tmp)//最多填一次减2
                    {
                        ans[cnt ++] = {r + 1,1};
                        tmp -= 2;
                    }
                }
                for(int i = 0; i < cnt; ++ i)
                printf("%d %d\n",ans[i].f,ans[i].s);
                for(int i = cnt; i < n; ++ i)
                printf("%d %d\n",x + n  + i,y + n  + i);
            }
             
        }
    }
    return 0;
}

你可能感兴趣的:(构造题)