0%

60901202-AGC027

D

需要构造一个 $N\times N$ 的矩阵 $A$ ,满足:$1\leq a_{i,j}\leq 10^{15}$ ; $a_{i,j}$ 互不相同;存在正整数 $m$ 满足对于任意矩阵中任意两个相邻的数字 $x,y$ 有 $\max(x,y)\mod \min(x,y)=m$

假设 $m=1$ ,考虑一个格子 $v$ ,如果它去模 $a,b,…$ 得到 $1$ ,则有 $\text{lcm}(a,b,…)|v-1$

假设格子 $v$ 单调,,则右边 > 左边,下面 > 上面,$\because a_{i,j} 模 a_{i-1,j},a_{i,j-1}$ 均为 $1$ ,$\therefore$ $\text{lcm}(a_{i,j-1},a_{i-1,j})|a_{i,j}-1$

可以固定第一行及第一列来得到整张表,但这种方法通常会超过 $10^{15}$

考虑题中:网格图中相邻的两个格子,那么建立二分图:黑白染色后相邻两点间连边

取出二分图中的一半 $A$ ,在其中任意填数并保证互不相同,对于另一部分 $B$ 的每个点,权值为与其相邻的点($A$ 中的点)上的数的 $\text{lcm}$ $+1$

但此时,二分图中有 $\frac{N^2}{2}=1.25\times 10^5$ 个点,最终的 $\text{lcm}$ 可达到 $(\frac{1.25\times 10^5}{2})^4$ ,即 $10^{19}$ ,超出范围

从数论的角度:考虑一个数相邻的四个位置,平均填数下每个数 $N^2$ 级别,若能使对角线上的两个格子的 $\gcd$ 是 $N$ 级别的,那么四个数的 $\text{lcm}$ 就是 $N^4=6.25\times 10^{10}$ ,能接受该级别

考虑令一条对角线上的元素有一个共同的 $\gcd$ 即可使对角线上相邻的两数有一个 $N$ 级别的 $\gcd$

那么进行以下操作:

  • 对每一条对角线,标记一个数 $w$
  • 对每一个格子 $a_{i,j}$ ,设经过他的两条对角线为 $d_1,d_2$ ,令 $a_{i,j}=w_{d_1}\times w_{d_2}$

为了保证所有数互不相同,取 $w$ 为最小的若干个素数

而二分图另一部分就是相邻 $4$ 个素数的乘积 $+1$

显然对角线条数 $<2\times N$ ,第 $500$ 个素数为 $3571$ ,第 $1000$ 个素数为 $7919$ ,其 $\text{lcm}=3571^2\times 7919^2<10^{15}$ ,则正确性得证

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
const int N=510;
int n;
int pn,c[7930],p[1054];
ll a[N][N];

inline void sieve(int n){
memset(c,-1,sizeof c);
for(rei i=2;i<=n;++i){
if(!~c[i]) p[pn]=i,c[i]=pn++;
for(rei j=0,v;(v=i*p[j])<=n && j<=c[i];++j) c[v]=j;
}
}

int main(){
int *q;
scanf("%d",&n),sieve(7929);
q=p+(n-1);
for(rei i=0;i<n;++i) for(rei j=0;j<n;++j) if((i^j)&1) a[i][j]=p[ (i+j-1)/2 ]*q[ (j-i+n-1)/2 ];
for(rei i=0;i<n;++i) for(rei j=0;j<n;++j) if(!((i^j)&1)){
ll &s=a[i][j]; s=1;
if (i || j) s*=p[ (i+j-2)/2 ];
if (i!=n-1 || j!=n-1) s*=p[ (i+j)/2 ];
if (i || j!=n-1) s*=q[ (j-i+n)/2 ];
if (i!=n-1 || j) s*=q[ (j-i+n-2)/2 ];
++s;
}
if(n==2) a[1][1]=a[1][1]*2-1;
for(rei i=0;i<n;++i) for(rei j=0;j<n;++j) printf("%lld%c",a[i][j],j==n-1 ? 10 : 32);
getchar(),getchar();
return 0;
}

E

给一个由 $a,b$ 组成的字符串,进行 $0$ 或若干次任意一种以下操作:将 $aa$ 转化为 $b$ ,将 $bb$ 转化为 $a$ ,求能得到多少种不同的串

这里有一个 $\text{trick}$ :将 $a$ 赋值为 $1$ ,$b$ 赋值为 $2$ ,那么所有字符对应的数之和模 $3$ 的结果不变,记该结果为 $K$

那么显然能得到一个结论:串 $s$ 转化为字符 $c$ 的充要条件是 $K(s)=K(c)$ 且 $s$ 不是长度大于 $1$ 的 $ab$ 交错的串

枚举 $t$ 的第一个字符,根据贪心,指定 $s$ 尽可能短的前缀

用 $f_i$ 表示 $s[1,…,i]$ 变成本质不同的串数量,允许 $3\mid K(s)$ 的串可以是空串

如此可以保证合法结果不漏:以 $s=abb$ 为例,$ab$ 变为空串,$b$ 仍是 $b$ ,合起来的结果 $b$ 恰好合法

考虑转移:

  • $K(s[1,…,i])=0$ 则 $s[1,…,i]$ 是空串

  • $t$ 的最后一个字符是 $s_i$ ,贪心的,分配一个 $s_i$ ,则有 $f_{i-1}$ 个以 $s_i$ 结尾的串

  • $t$ 的最后一个字符不是 $s_i$ ,找一个最短的后缀分配:

    设该后缀为 $s[j+1,…,i]$ ,那么有 $f_j$ 个以 $s_i$ 的“互补”结尾的串

那么有转移:$f_i=[\ 3\mid K(s[1,…,i]) \ ]+f_{i-1}+f_j$ ,其中 $j=\max\{p\mid K(s[p+1,…,i])+K(s_i)=3\}$

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
const int N=1e5+100;
const int mod=1e9+7;
int n,f[N],S[N],B[3]={1};
char s[N];
bool flag=0;

inline void fix(int &x){ x>=mod ? x-=mod : 0;}

int main(){
scanf("%s",s),n=strlen(s);
rei i;
for(i=1;i<n && s[i]!=s[i-1];++i);
if(i==n) return puts("1"),0; //特判ab交错的串
f[0]=1;
for(rei i=1;i<=n;++i){
S[i]=(S[i-1]+s[i-1])%3;// = 3|K(s[1,...,i])
f[i]=f[i-1]+!S[i];
fix(f[i]+=B[ S[i]^S[i-1]^3 ]);//j的寻找:对每个前缀的 K(s[1,...,i] 记录dp值)
B[ S[i] ]=f[i];
}
if(!S[n]) fix(f[n]+=mod-1);//最后的空串造不出来
printf("%d\n",f[n]);
getchar(),getchar();
return 0;
}

F

给定两个 $N$ 节点的树 $A,B$ ,对 $A$ 执行若干次操作,每次选择一个叶节点,删除其边并连向任意一个另外的点,每个点只能被选择一次 ,求使 $AB$ 相同的最小操作次数

一个点只能操作一次的性质 :规定一个点作为根,并规定不能操作它,问题则转化为有根树上的问题

此时,一个点被操作当且仅当它在两棵树中父亲不同

有约束条件:

  • 若点 $u$ 不需操作,则 $fa_u$ 不能被操作
  • $u$ 必须在 $faA_u$ 前被操作,$faB_u$ 后被操作

利用第一个,枚举 $u$ 判断是否满足

第二个用拓扑排序得到合法方案

枚举第一个被操作的点 $u$ 后,以 $u$ 作为根重读上述步骤即可

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
const int N=57;
int fa[N],ndeg[N],deg[N],vis[N],mp[N][N],n,ans;
vector<int> A[N],B[N];

void dfs(int u,int fath){
fa[u]=fath; if(vis[fath] && mp[u][fath]) vis[u]=1;
for(int v:B[u]) if(v!=fath) dfs(v,u);
}

inline void clear(){
memset(mp,0,sizeof mp);
for(rei i=1;i<=n;++i) A[i].clear(),B[i].clear();
}

int main(){
rei T; scanf("%d",&T);
while(T--){
scanf("%d",&n); ans=n+1;
clear();
for(rei i=1,u,v;i<n;++i) scanf("%d%d",&u,&v),mp[u][v]=mp[v][u]=1,A[u].push_back(v),A[v].push_back(u);
for(rei i=1,u,v;i<n;++i) scanf("%d%d",&u,&v),B[u].push_back(v),B[v].push_back(u);
for(rei i=1;i<=n;++i) ndeg[i]=A[i].size();
for(rei i=1;i<=n;++i){
rei cost=0,u;
memset(vis,0,4*n+4);
vis[i]=1; dfs(i,0); memcpy(deg+1,ndeg+1,4*n);
while(true){
for(u=1;u<=n;++u) if(!vis[u] && deg[u]==1 && vis[ fa[u] ]) break;
if(u>n) break; vis[u]=1,++cost;
for(int v:A[u]) --deg[v];
}
for(u=1;u<=n;++u) if(!vis[u]) break;
if(u>n) ans=min(ans,cost);
if(A[i].size()>1 || u>n || ans<=n) continue;
memset(vis,0,4*n+1);
vis[0]=1,memcpy(deg+1,ndeg+1,4*n);
while(true){
for(u=1;u<=n;++u) if(!vis[u] && deg[u]<=1 && vis[ fa[u] ]) break;
if(u>n) break; vis[u]=1;
for(int v:A[u]) --deg[v];
}
for(u=1;u<=n;++u) if(!vis[u]) break;
if(u>n) ans=n;
}
printf("%d\n",ans>n ? -1 : ans);
}
getchar(),getchar();
return 0;
}