非常规题做题记录
Imaginative · · 个人记录
A.Ancestor Relation
题面
拿到题目想到第一件事一定是先判无解。
容易发现,合法的
那么无解的第一个条件就很明显,
假设
因此我们便找到第二个判无解条件,满足上面两条件但
接下来考虑计算答案,但是两个不怎么相同的
当
剩下的因为没有在链上的性质无法交换位置,因此位置被固定无法贡献答案。
可以用bitset优化到
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=405;
const int mod=998244353;
int n,t;
int a[N][N];
int tag[N],lc[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>t;
lc[0]=1;
for(int i=1;i<=400;i++) lc[i]=lc[i-1]*i,lc[i]%=mod;
while(t--){
memset(tag,0,sizeof(tag));
int ans=1;
cin>>n;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j];
bool flag=0;
for(int i=1;i<=n;i++) if(!a[1][i]){
cout<<0<<"\n";
flag=1;
break;
}
if(flag) continue;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
bool f1=1,f2=1,f3=1;
for(int w=1;w<=n;w++){
if(a[i][w]!=a[j][w]) f3=0;
if(a[i][w]&&!a[j][w]) f1=0;
if(!a[i][w]&&a[j][w]) f2=0;
}
if(f3) continue;
if((f1&&!f2)||(!f1&&f2)){if(!a[i][j]) flag=1;}
if((f1&&f2)||(!f1&&!f2)){if(a[i][j]) flag=1;}
if(flag==1) break;
}
if(flag==1) break;
}
if(flag){
cout<<0<<"\n";
continue;
}
for(int i=1;i<=n;i++){
if(tag[i]) continue;
int p=0;
if(i>1) p++;
for(int j=i+1;j<=n;j++){
bool f=1;
for(int k=1;k<=n;k++) if(a[i][k]!=a[j][k]) f=0;
if(f) tag[j]=1,p++;
}
if(p) ans*=lc[p],ans%=mod;
}
cout<<ans<<"\n";
}
return 0;
}
B.Tree Restoring
题面
容易联想到直径。直径两端的点相隔的距离一定是最远的,并且存在的点一定是两个及以上,因此想到第一种判无解。
根据树的性质及观察样例可知,最远距离在树上一定是连续的,不可能出现父亲最远距离是
-
如果最远距离是偶数,那么直径所在链形成的数一定是:
a,a-1,a-2~~~...~~~a-k~~~...~~~a-1,a 根据直径性质可得且
a-k 只有一个 -
如果最远距离是奇数,则是:
a,a-1,a-2~~~...~~~a-k,a-k~~~...~~~a-1,a 同理,且
a-k 有且仅有两个
剩余的点我们可以挂在直径上,它的最远距离就是它挂着点的最远距离加1,这也是为什么奇数时
用桶判个数即可,这就是第三种判无解(其实可以涵盖第二种)
C.Vasya And The Matrix
题面
一种很巧妙的做法。
无解很简单,整个矩形异或两遍一定是
构造题就要构造的越简单越好,最好是每行只有一个数。假设我们把所有的数全部填在同一列就很好满足行的异或和,满足列的异或和就是放在同一行。
那么我们不妨让它们都放在最后一行和最后一列(其它也可以,只需保证行列互不干涉)。最后一个位置暂时不填,它需要满足两个约束,设答案矩形为
因为一个合法矩形异或两遍一定是
根据我们的填法
代回移项,观察两式
左式相异或值为