[构造 随机 生成树计数] UOJ #75 【UR #6】智商锁

题解

大概意思是拆成4个12 瞎随机个千把个
找出四个使得abcd=K
这个meet in middle 就好了

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

const int P=998244353;
const int N=15;

inline ll Pow(ll a,int b){
  ll ret=1; for (;b;b>>=1,a=a*a%P) if (b&1) ret=ret*a%P; return ret;
}
inline ll Inv(ll a){
  return Pow(a,P-2);
}
ll a[N][N];
inline ll det(int n){
  int f=0;
  for (int i=1;i<=n;i++){
    int k=0;
    for (int j=i;j<=n;j++) if (a[j][i]) { k=j; break; }
    if (i!=k) { f^=1; for (int j=1;j<=n;j++) swap(a[i][j],a[k][j]); }
    for (int j=i+1;j<=n;j++){
      ll t=a[j][i]*Inv(a[i][i])%P;
      for (int k=i;k<=n;k++)
    (a[j][k]+=P-t*a[i][k]%P)%=P;
    }
  }
  ll ret=1;
  for (int i=1;i<=n;i++) ret=ret*a[i][i]%P;
  return f?(P-ret)%P:ret;
}

const int T=1000;

int n=12;
int w[T+5][N][N],ed[T+5];
ll cnt[T+5];

inline void Rand(int (*w)[N],ll &cnt,int &ed){
  for (int i=1;i<=n;i++)
    for (int j=i+1;j<=n;j++)
      w[i][j]=w[j][i]=rand()&1,ed+=w[i][j];
  for (int i=1;i<=n;i++) a[i][i]=0;
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++){
      if (i==j) continue;
      a[i][j]=(P-w[i][j])%P;
      a[i][i]+=w[i][j];
    }
  cnt=det(n-1);
}
inline void print(int _a,int _b,int _c,int _d){
  int a[4]={_a,_b,_c,_d};
  printf("%d %d\n",n<<2,ed[_a]+ed[_b]+ed[_c]+ed[_d]+3);
  for (int t=0;t<4;t++)
    for (int i=1;i<=n;i++)
      for (int j=i+1;j<=n;j++)
    if (w[a[t]][i][j])
      printf("%d %d\n",t*n+i,t*n+j);
  for (int i=1;i<4;i++)
    printf("%d %d\n",(i-1)*n+1,i*n+1);
}

const int MOD=1000007;
const int M=1000005;
int head[MOD],inum;
int x[M],y[M],v[M],next[M];

inline int find(ll k){
  for (int p=head[k%MOD];p;p=next[p])
    if (v[p]==k)
      return p;
  return 0;
}

ll K;

int main(){
  srand(10086);
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  for (int i=1;i<=T;i++) Rand(w[i],cnt[i],ed[i]);
  for (int i=1;i<=T;i++)
    for (int j=1;j<=T;j++){
      int p=++inum; ll t=cnt[i]*cnt[j]%P;
      if (t) x[p]=i,y[p]=j,v[p]=t,next[p]=head[t%MOD],head[t%MOD]=p;
    }
  int _T;
  scanf("%d",&_T);
  while (_T--){
    scanf("%lld",&K);
    if (K==0) { printf("2 0\n"); continue; }
    int f=0;
    for (int i=1;i<=T && !f;i++)
      for (int j=1;j<=T;j++){
    if (cnt[i]*cnt[j]%P==0) continue;
    ll t=K*Inv(cnt[i]*cnt[j]%P)%P; int p;
    if (p=find(t)){
      print(i,j,x[p],y[p]);
      f=1; break;
    }
      }
    if (!f) printf("QwQ\n");
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值