AtCoder Beginner Contest 317

https://atcoder.jp/contests/abc317

F - Nim

题意

给定整数 n,a1,a2,a3n,a_1,a_2,a_3。求解符合以下条件的 (x1,x2,x3)(x_1,x_2,x_3) 的个数:

  • 1xin1\leq x_i\leq n
  • xix_iaia_i 的倍数
  • x1x2x3=0x_1\oplus x_2\oplus x_3=0

结果对 998244353998244353 取模。

数据范围:1n1018,1ai101\leq n\leq 10^{18},1\leq a_i\leq 10

题解

数位dp,但是状态设计又和常规的有些不同。感觉还是见得太少了,也不够灵活。

fi,j1,j2,j3,r1,r2,r3f_{i,j_1,j_2,j_3,r_1,r_2,r_3} 表示处理到二进制下从低到高的第 ii 位,第 1,2,31,2,3 个数的低 ii 位是否不超过 nn 的低 ii 位,第 1,2,31,2,3 个数对 a1,a2,a3a_1,a_2,a_3 取模结果为 r1,r2,r3r_1,r_2,r_3 的方案数。转移时只需要考虑一下三个数该位置填什么(保证异或和为 00)。最终 f60,1,1,1,0,0,0f_{60,1,1,1,0,0,0} 就是答案。不过结果还需要去除掉某些数为 00 的情况。只需要减去 1+nlcm(a2,a3)+nlcm(a1,a3)+nlcm(a1,a2)1+\lfloor\frac{n}{\mathrm{lcm}(a_2,a_3)}\rfloor+\lfloor\frac{n}{\mathrm{lcm}(a_1,a_3)}\rfloor+\lfloor\frac{n}{\mathrm{lcm}(a_1,a_2)}\rfloor 就可以了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p=998244353;
int a[3];
ll n,f[65][2][2][2][15][15][15];
ll lcm(ll x,ll y) {return x*y/__gcd(x,y)%p;}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>a[0]>>a[1]>>a[2];
for(int i=0;i<=1;i++)
for(int j=0;j<=1;j++)
for(int k=0;k<=1;k++)
if((i^j^k)==0) f[0][i<=(n&1)][j<=(n&1)][k<=(n&1)][i%a[0]][j%a[1]][k%a[2]]++;
for(int t=0;t<60;t++)
for(ll i=0;i<=1;i++)
for(ll j=0;j<=1;j++)
for(ll k=0;k<=1;k++)
for(int t1=0;t1<=1;t1++)
for(int t2=0;t2<=1;t2++)
for(int t3=0;t3<=1;t3++)
for(int t4=0;t4<a[0];t4++)
for(int t5=0;t5<a[1];t5++)
for(int t6=0;t6<a[2];t6++)
if((i^j^k)==0)
{
int bit=n>>(t+1)&1;
f[t+1][(i<=bit&&t1)||i<bit][(j<=bit&&t2)||j<bit][(k<=bit&&t3)||k<bit][((i<<(t+1))+t4)%a[0]][((j<<(t+1))+t5)%a[1]][((k<<(t+1))+t6)%a[2]]+=f[t][t1][t2][t3][t4][t5][t6]%=p;
}
ll ans=f[60][1][1][1][0][0][0];
ans=(ans-1+p)%p;
ans=(ans-n/lcm(a[1],a[2])%p+p)%p;
ans=(ans-n/lcm(a[0],a[2])%p+p)%p;
ans=(ans-n/lcm(a[0],a[1])%p+p)%p;
cout<<ans<<'\n';
return 0;
}

G - Rearranging

题意

给出一个 n×mn\times m 的矩阵,里面 1n1\sim n 每个数恰好出现了 mm 次。现在可以重排每一行,要求最终的矩阵每列都是 1n1\sim n 的排列。输出最终的答案矩阵,若不行,则输出 1-1

数据范围:1n,m1001\leq n,m\leq 100

题解

每行可以重排,但是一行一共有哪些数是确定的。所以可以想到这样建图:建立一张二分图,iiai,ja_{i,j} 连边(ii 为左部点,ai,ja_{i,j} 为右部点)。跑一次二分图最大匹配,这样就确定了第一列的答案。将这些匹配边删去,然后再继续跑最大匹配。一共跑 mm 次,就做完了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <bits/stdc++.h>
using namespace std;
const int N=105;
int a[N][N],ans[N][N];
const int inf=0x3f3f3f3f;
struct edge
{
int to,nxt;
int c;
}e[2*N*N];
int cnt=1,head[2*N];
int s,t;
int d[2*N],cur[2*N];
void add(int u,int v,int c)
{
e[++cnt]={v,head[u],c};head[u]=cnt;
e[++cnt]={u,head[v],0};head[v]=cnt;
}
bool bfs()
{
memset(d,0,sizeof(d));
queue<int> q;
d[s]=1;q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(d[v]==0&&e[i].c)
{
d[v]=d[u]+1;q.push(v);
if(v==t) return true;
}
}
}
return false;
}
int dfs(int u,int mf)
{
if(u==t) return mf;
int sum=0;
for(int i=cur[u];i;i=e[i].nxt)
{
cur[u]=i;
int v=e[i].to;
if(d[v]==d[u]+1&&e[i].c)
{
int res=dfs(v,min(mf,e[i].c));
e[i].c-=res;
e[i^1].c+=res;
sum+=res;
mf-=res;
if(mf==0) break;
}
}
if(sum==0) d[u]=0;
return sum;
}
int dinic()
{
int flow=0;
while(bfs())
{
memcpy(cur,head,sizeof(cur));
flow+=dfs(s,inf);
}
return flow;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
add(i,n+a[i][j],1);
}
s=0,t=2*n+1;
for(int i=1;i<=n;i++) add(s,i,1),add(i+n,t,1);
for(int i=1;i<=m;i++)
{
if(dinic()<n)
{
cout<<"NO"<<'\n';
return 0;
}
for(int j=3;j<=2*n*m+1;j+=2)
{
if(e[j].c==1)
{
ans[e[j].to][i]=e[j^1].to-n;
e[j].c=e[j^1].c=0;
}
}
for(int j=2*n*m+3;j<=cnt;j+=2)
if(e[j].c==1)
{
e[j^1].c=1;
e[j].c=0;
}
}
cout<<"Yes"<<'\n';
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cout<<ans[i][j]<<" \n"[j==m];
return 0;
}

AtCoder Beginner Contest 317
https://je3ter.github.io/2023/08/29/ACM/AtCoder Beginner Contest 317/
作者
Je3ter
发布于
2023年8月29日
许可协议