ARC143 小记
A
不妨 \(A<B<C\),判断若 \(A+B<C\) 无解,否则输出 \(C\)
B
题意:给定一个 \(N\times N\) 方形,填入 \(1\) 到 \(N^2\),求每一列的最大值与每一行的最小值不重复的方案数
设最大值与最小值重复的点为坏点,那么假设坏点有至少两个,对于坏点 \(a,b\) ,设它们行列分别交于 \(c,d\) ,那么 \(a<c<b,b<d<a\) 矛盾
所以至多一个坏点,枚举坏点的值,再往坏点所在行和列填数,剩下的随意排列,即可求出不合法方案总和
#include <cstdio>
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int P=998244353;
const int N=250003;
int fac[N],fiv[N],inv[N];
int C(int n,int k){
if(k<0||k>n) return 0;
return 1ll*fac[n]*fiv[n-k]%P;
}
int main(){
int n=read();
fac[0]=fiv[0]=inv[1]=1;
for(int i=1;i<=n*n;++i){
if(i>1) inv[i]=1ll*inv[P%i]*(P-P/i)%P;
fac[i]=1ll*fac[i-1]*i%P;
fiv[i]=1ll*fiv[i-1]*inv[i]%P;
}
int res=fac[n*n];
for(int i=1;i<=n*n;++i)
res=(res+P-1ll*n*n*C(i-1,n-1)%P*C(n*n-i,n-1)%P*fac[n*n-2*n+1]%P)%P;
printf("%d\n",res);
return 0;
}
C
题意:有若干堆石子,甲可以选择若干堆每堆取走 \(X\) 个,乙可以选择若干堆每堆取走 \(Y\) 个,甲乙每次操作必须取至少一堆,谁不能取就输,甲先操作,问谁必胜
妙题,考虑每堆石子对 \(X+Y\) 取模余数 \(a_i\),若 \(\forall i,0\leq a_i < X\) ,那么乙一定必胜,因为若甲选择了取某些堆,那么这些堆一定有 \(\forall i,X\leq a_i < X+Y\) ,这是乙再次在这些堆各取一次,直到所有石子数都比 \(X\) 小,甲输
现在假设 \(\exists i,X\leq a_i < X+Y\) ,那么对于每一堆石子,都考虑取/不取能否使 \(0 \leq a_i<Y\) ,如果存在一堆石子无论甲取/不取都无法满足条件,此时一定有 \(X>Y\) ,乙再次合理操作即可把甲压到必败态,此时甲必败
#include <cstdio>
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
int main(){
int n=read(),a=read(),b=read();
if(a>b){
for(int i=1;i<=n;++i){
int t=read();
if(t%(a+b)<b) continue;
if(t>=a&&(t-a)%(a+b)<b) continue;
puts("Second");
return 0;
}
puts("First");
return 0;
}
else{
for(int i=1;i<=n;++i){
int t=read();
if(t%(a+b)<a) continue;
puts("First");
return 0;
}
puts("Second");
return 0;
}
return 0;
}
D
题意:无向图有 \(2N\) 个节点,第 \(i\) 个和第 \(i+N\) 个节点相连,给出若干个 \(a_i,b_i\) ,选择连 \((a_i+N,b_i)\) 或 \((a_i,b_i+N)\) ,问如何选择可以让图的桥最少
注意到对于 \((a_i,b_i)\) 这类的边,如果其成为桥无论怎么选边它都是桥,考虑减少形如 \((i,i+N)\) 这样的桥
那么可以选择造环,在 DFS 树上尽可能地覆盖更多边即可,一遍 DFS 搞定
#include <cstdio>
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int N=200003;
int a[N],b[N];
int n,m;
int hd[N],ver[N<<1],nxt[N<<1],id[N<<1],tot;
bool dir[N<<1],sd[N],ok[N];
void add(int u,int v,int w,bool d){nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;id[tot]=w;dir[tot]=d;}
bool vis[N];
int rk[N],num;
void dfs(int u){
vis[u]=1;rk[u]=++num;
for(int i=hd[u];i;i=nxt[i]){
if(!ok[id[i]]) sd[id[i]]=dir[i],ok[id[i]]=1;
if(!vis[ver[i]]) dfs(ver[i]);
}
}
int main(){
n=read();m=read();
for(int i=1;i<=m;++i) a[i]=read();
for(int i=1;i<=m;++i) b[i]=read();
for(int i=1;i<=m;++i){
add(a[i],b[i],i,0);
add(b[i],a[i],i,1);
}
for(int i=1;i<=n;++i) if(!vis[i]) dfs(i);
for(int i=1;i<=m;++i) if(sd[i]) putchar('1');else putchar('0');
putchar('\n');
return 0;
}
E
题意:一棵树,树上有硬币,初始有正面有反面,一次操作可以选择一个正面朝上的硬币移除,然后翻转树上所有相邻节点上的硬币,问如何操作可以移除所有硬币
考虑每一个硬币被移除时刻 \(d_u\) ,那么对于一个原本正面朝上的硬币,相邻节点一定有偶数个 \(d_i<d_u\) ,反面则有奇数个
对于叶子来说,它与父亲的时间戳大小关系已经确定,从下往上递推既可求出每一个节点和父亲的时间戳大小关系
如果根不满足奇偶性条件判无解,否则对于所有的时间戳大小关系建图,用堆跑拓扑排序,做完
#include <cstdio>
#include <queue>
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int N=200003;
priority_queue< int,vector<int>,greater<int> > q;
int n,rk,o[N];
int hd[N],ver[N<<1],nxt[N<<1],tot;
int deg[N],ft[N],ghd[N],gver[N],gnxt[N],gtot;
bool s[N];
void add(int u,int v){nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;}
void gadd(int u,int v){gnxt[++gtot]=ghd[u];ghd[u]=gtot;gver[gtot]=v;}
bool dfs(int u,int fa){
ft[u]=fa;
for(int i=hd[u],v;i;i=nxt[i])
if((v=ver[i])^fa)
s[u]^=!dfs(v,u);
return s[u];
}
int main(){
n=read();
for(int i=1;i<n;++i){
int u=read(),v=read();
add(u,v);add(v,u);
}
char c=getchar();
while(c!='W'&&c!='B') c=getchar();
for(int i=1;i<=n;++i) s[i]=(c=='B'),c=getchar();
if(dfs(1,0)){puts("-1");return 0;}
for(int i=2;i<=n;++i)
if(s[i]) gadd(ft[i],i),++deg[i];
else gadd(i,ft[i]),++deg[ft[i]];
for(int i=1;i<=n;++i) if(!deg[i]) q.push(i);
while(!q.empty()){
int u=q.top();q.pop();
printf("%d ",u);
for(int i=ghd[u];i;i=gnxt[i])
if(!--deg[gver[i]]) q.push(gver[i]);
}
putchar('\n');
return 0;
}