题解:P10441 [JOIST 2024] 乒乓球 / Table Tennis
思路
首先,如果一个点
我们总共可能的三元环数量是
这个东西的最小值可以取到
我们希望让这个图少一些三元环,那么能不能通过修改一些东西让这个图减少恰好一个三元环呢?事实上是可以的,就是你考虑如果一条边连接了两个
但是你发现,我要减少的边的数量可能是
仔细考虑一下,发现一张竞赛图我增加一个点,至多增加
于是我们只需要减少
我们需要动态维护
代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned int
#define N 5005
#define mod 998244353
#define pii pair<int,int>
#define x first
#define y second
#define inf 2e18
using namespace std;
int Tc=1;
ll n,m;
bool st[N][N];
ll C(int n,int m){
if(n<m)return 0;
if(m==2)return 1ll*n*(n-1)/2;
return 1ll*n*(n-1)/2*(n-2)/3;
}
ll f(int n){
ll res=C(n,3);
res-=1ll*n*C((n-1)/2,2);
if(n%2==0)res-=1ll*(n/2)*(C(n/2,2)-C((n-1)/2,2));
return res;
}
int d[N];
pii g[N],h[N];
void solve(int cs){
if(!cs)return;
cin>>n>>m;
if(f(n)<m){
cout<<"No\n";
return;
}
for(int i=1;i<=n;i++){
d[i]=0;
for(int j=1;j<=n;j++){
st[i][j]=0;
}
}
cout<<"Yes\n";
for(int i=1;i<=n;i++){
if(f(i)<m)continue;
m=f(i)-m;
for(int j=1;j<=i;j++){
d[j]=(i-1)/2;
if(i%2==0&&j<=i/2)d[j]++;
}
for(int j=1;j<=n;j++){
d[j]+=min(n-i,n-j);
}
while(m){
for(int l=1,r=1;l<=i;){
r=l;
while(r<i&&d[r+1]==d[r])r++;
int tmp=r;
while(l<r&&m>0){
m--;
d[l]--;d[r]++;
l++;r--;
}
r=tmp;
l=r+1;
}
}
break;
}
sort(d+1,d+n+1,greater<int>());
// for(int i=1;i<=n;i++){
// cerr<<d[i]<<' ';
// }
// cerr<<'\n';
for(int i=1;i<=n;i++){
g[i]={d[i],i};
}
for(int k=1;k<=n;k++){
int i=g[k].y;
int fj=n;
for(int j=n;j>k;j--){
auto t=g[j];
if(!d[i]){
d[t.y]--;
g[j].x--;
st[t.y][i]=1;
}
else{
fj=j-1;
d[i]--;
st[i][t.y]=1;
}
}
int l=k+1,r=fj+1,cur=k;
// cerr<<"dududu "<<l<<' '<<r<<'\n';
while(l<=fj&&r<=n){
if(g[l].x>=g[r].x){
h[++cur]=g[l++];
}
else{
h[++cur]=g[r++];
}
}
while(l<=fj)h[++cur]=g[l++];
while(r<=n)h[++cur]=g[r++];
for(int j=k+1;j<=n;j++){
g[j]=h[j];
// cerr<<g[j].x<<' ';
}
// cerr<<'\n';
}
// for(int i=1;i<=n;i++){
// cerr<<d[i]<<' ';
// }
// cerr<<'\n';
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++){
cout<<st[i][j];
}
cout<<'\n';
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// init();
cin>>Tc;
for(int cs=1;cs<=Tc;cs++){
solve(cs);
}
// cerr<<clock()*1.0/CLOCKS_PER_SEC<<'\n';
// cerr<<((&ed)-(&st))/1048576.0<<'\n';
return 0;
}