제출 #1350235

#제출 시각아이디문제언어결과실행 시간메모리
1350235alexddTable Tennis (JOI24_tabletennis)C++20
100 / 100
467 ms209256 KiB
#include <bits/stdc++.h>
using namespace std;
#define int long long

const int INF = 1e12;

int comb2(int x)
{
    return x * (x-1) / 2;
}

int n,m;


bool verif(vector<int> v)
{
    int pref = 0;
    for(int i=1;i<=n;i++)
    {
        if(i > 1 && v[i-1] > v[i])
            return 0;
        pref += v[i];
        if(pref < comb2(i))
            return 0;
    }
    if(pref != comb2(n))
        return 0;
    return 1;
}

int calc(vector<int> v)
{
    int tot = 0;
    for(int i=1;i<=n;i++)
        tot += comb2(v[i]);
    return tot;
}

bool done;
vector<int> sol;

int fr[5005];
void recursiv(vector<int> newv)
{

    for(int i=0;i<=n;i++)
        fr[i]=0;
    for(int i=1;i<=n;i++)
        fr[newv[i]]++;

    int mycalc = calc(newv);

    int minfr = 0, maxfr = n;
    while(fr[minfr] == 0)
        minfr++;
    while(fr[maxfr] == 0)
        maxfr--;

    while(1)
    {
        if(mycalc == m)
        {
            sol.clear();
            sol.push_back(0);
            for(int i=0;i<=n;i++)
                for(int j=1;j<=fr[i];j++)
                    sol.push_back(i);
            done = 1;
            return;
        }


        assert(minfr <= maxfr);

        if(minfr + 1 < maxfr)
        {
            int newcalc = mycalc - maxfr + 1 + minfr;
            if(newcalc >= m)
            {
                fr[maxfr]--;
                fr[maxfr - 1]++;

                if(fr[maxfr] == 0)
                    maxfr--;

                fr[minfr]--;
                fr[minfr + 1]++;

                if(fr[minfr] == 0)
                    minfr++;

                mycalc = newcalc;
                continue;
            }
        }


        int closest = abs(mycalc - m), c_i, c_j, c_calc = -1;
        for(int idk=n-1;idk>=2;idk--)
        {
            for(int i=0;i+idk<=n-1;i++)
            {
                int j = i+idk;

                if(fr[i] > 0 && fr[j] > 0)
                {
                    int newcalc = mycalc - j + 1 + i;
                    int newdist = abs(m - newcalc);

                    if(newdist < closest)
                    {
                        closest = newdist;
                        c_i = i;
                        c_j = j;
                        c_calc = newcalc;
                    }
                }
            }
        }

        if(c_calc == -1)
            return;

        mycalc = c_calc;
        fr[c_i]--;
        fr[c_i + 1]++;

        fr[c_j]--;
        fr[c_j - 1]++;

        minfr = 0, maxfr = n;
        while(fr[minfr] == 0)
            minfr++;
        while(fr[maxfr] == 0)
            maxfr--;
    }
}

int mat[5005][5005];
void reconstruct(vector<int> v)
{
    assert(verif(v));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            mat[i][j] = 0;

    vector<pair<int,int>> aux;
    for(int i=1;i<=n;i++)
        aux.push_back({v[i], i});
    while(!aux.empty())
    {
        sort(aux.begin(), aux.end());
        if(aux.back().first == 0)
            break;
        assert(aux.back().first > 0);

        for(int i=0;i<aux.back().first;i++)
        {
            mat[aux.back().second][aux[i].second] = 1;
        }
        for(int i=aux.back().first;i<(int)aux.size()-1;i++)
        {
            aux[i].first--;
            mat[aux[i].second][aux.back().second] = 1;
        }
        aux.back().first = 0;
        aux.pop_back();
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            assert(mat[i][j] + mat[j][i] == 1);
            if(mat[i][j] + mat[j][i] != 1)
            {
                //cerr<<i<<" "<<j<<": "<<mat[i][j]<<" & "<<mat[j][i]<<" zzz\n";
            }
        }
    }

    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<i;j++)
            cout<<mat[i][j];
        cout<<"\n";
    }
}

void solve()
{
    cin>>n>>m;
    m = n * (n-1) * (n-2) / 6 - m;

    //get close to m then do backtrack
    done = 0;

    vector<int> init(n+2,0);
    for(int i=1;i<=n;i++)
        init[i] = i - 1;

    assert(verif(init));

    if(m > calc(init))
    {
        cout<<"No\n";
        return;
    }

    recursiv(init);



    if(!done)
    {
        cout<<"No\n";
        return;
    }

    assert(calc(sol) == m);

    //for(int i=1;i<=n;i++) cerr<<sol[i]<<" ";cerr<<"sol\n";

    cout<<"Yes\n";
    reconstruct(sol);
}

signed main()
{
    ios_base::sync_with_stdio(0);cin.tie(0);
    int t;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

/*

for each "bad" tuple (x,y,z) of nodes,
exactly one of x,y,z will have outgoing edges towards the other 2
=> M = total - sum(comb(cnt_outgoing(x), 2))

*/
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...