A.小D与原题
题意:
有$n$个点,求$n-1$个完美匹配,且其中不出现相同的边。
$nle 10^3$
分析一:
打暴力/手玩找到规律。
把匹配放到方格图上,给属于同一个完美匹配的方格染上同样的颜色,发现两个性质:
①最后一列第一行填$n$,之后往下从小到大填完偶数,再从小到大填完奇数;
②$forall iin [1,n-1]$,从$(1,i+1)$开始往左下方填,要填$(j,0)$时,转到$(j,n-1)$继续。
然后每个匹配$(x,y)$按照$x<y$输出即可。
分析二:
考虑对于第$i$组组完美匹配,我们可以让它的所有$n/2$个匹配的某种特征量都相同且为$i$。
考虑令匹配$(x,y)$中$x+y=i$。这里为了避开主对角线,令点由$0sim n-1$标号,那么一组完美匹配有:
①所有$x+yequiv i (mod n-1),;0le x,y<\, n-1$;
②一个$2pequiv i (mod n-1)$。
发现和分析一里的规律吻合。
实现(100分):

#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<set> #include<map> #define IL inline #define RG register using namespace std; const int N=1e3; int n; int main(){ freopen("problem.in","r",stdin); freopen("problem.out","w",stdout); scanf("%d",&n); for(int i=1;i<n;i++){ for(int j=0;j<n-1;j++){ int k=(i+n-1-j)%(n-1); if(j==k) printf("%d %d ",j+1,n); else if(j<k) printf("%d %d ",j+1,k+1); } printf(" "); } return 0; }
小结:
精准分配的时候,可以运用这种“关联特征量”的思路。比如:电影院放映厅按照单双号将人群分流到两个大门,人们按照行号寻找座位。
一句题外话:下午讲题(B组)的时候,没有投影分数榜,结果我莽上去讲“第一题的某种直观理解方法”(分析一),没有发现下面一片尴尬的沉默,直到讲解第四题的时候我才惊觉自己走错了片场。监介。
B.小D与随机
题意:
给出一棵$n$个节点的有根树,点的权值序列$w{n}$是一个$1sim n$的排列。定义点$i$是“好点”意为,$w_i$在根到$i$的链上最大。假设某个确定排列会确定树上的$c$个“好点”,那么这个排列的贡献是$k^c$。给出$k$,求所有排列的贡献总和。对$998244353$取模。
对于$20\%$的数据,$nle 20$。
对于另$40\%$的数据,给出一条链。
对于$100\%$的数据,$nle 5000$。
分析一:
考虑链上怎么做。
$mathscr{OI\_Killer}$:打表找规律,很容易就找到了。我也没仔细证。
花了$4h+$撕烤规律+推导然而还是没能找到推法的$mathtt{Hansue}$表示:这规律真$TM$难推导好吧。
设$f_i$是$n=i$时的答案,那么$f_n=f_{n-1} imes (k+n-1)$,且$f_1=k$。
实现(40分):

#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<set> #include<map> #define IL inline #define RG register using namespace std; typedef long long LL; const int N=5000; const LL mod=998244353; int n; LL k; int main(){ freopen("random.in","r",stdin); freopen("random.out","w",stdout); scanf("%d%lld",&n,&k); LL ans=k; for(int i=2;i<=n;i++) ans=ans*(k+i-1)%mod; printf("%lld",ans); return 0; }
小结:
实在不行,打表找规律√
C.小D与游戏
题意:
字符集为${a,b,c}$。对于一个字符串,定义一个操作是将其相邻某两个不同字符,替换成另一个字符。
给出原串,求能够拓展出的字符串集的大小。
分析:
令$a=0,b=1,c=2$,设$g(s)=sumlimits_i s_i (mod 3)$,发现一个操作不会改变$g(s)$,且操作之后至少会有一组相邻的相同字符。
那么所有长度以及$g()$相同的,且至少有一组相邻的相同字符的字符串,它们互相连通。
那么一个不存在相邻相同字符的字符串,随意操作一次,就进入了上述连通块。
现在撕烤如何计算长度为$n$,$g()$和给定串相同的答案。
考虑递推计算,设$f[n][sum][c][p]$表示长度为$n$,$g()=sum$,末位字符是$c$,存在相邻相同字符的情况是$p$,那么$$f[i+1][(s0+k)\%3][k][p||(k==x)]+=f[i][s0][x][p]$$
初值是$f[1][i][i][0]=1$。
实现(100分):

#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<set> #include<map> #define IL inline #define RG register using namespace std; typedef long long LL; const int N=2e5; const LL mod=998244353; IL bool insigma(char ch){ return 'a'<=ch&&ch<='c'; } int n,s[N+3]; IL void init(){ char ch; while(!insigma(ch=getchar())); s[n=0]=ch-'a'; n++; while(insigma(ch=getchar())) s[n++]=ch-'a'; } IL bool check0(){ for(int i=1;i<n;i++) if(s[i]!=s[i-1]) return false; return true; } IL void sol1(){ int ans; if(n==1) ans=1; else if(n==2) ans=2; else if(n==3){ if(s[0]!=s[1]&&s[0]!=s[2]&&s[1]!=s[2]) ans=3; else if(s[0]==s[1]||s[1]==s[2]) ans=6; else ans=7; } printf("%d",ans); } IL bool check1(){ for(int i=1;i<n;i++) if(s[i]==s[i-1]) return true; return false; } IL void add(LL &x,LL y){ x=(x+y)%mod; } IL int fsum(){ int ret=0; for(int i=0;i<n;i++) ret=(ret+s[i])%3; return ret; } LL f[N+3][3][3][2]; IL void sol2(){ for(int i=0;i<3;i++) f[1][i][i][0]=1; for(int i=1;i<n;i++) for(int s0=0;s0<3;s0++) for(int j=0;j<3;j++) for(int k=0;k<3;k++) for(int p=0;p<2;p++) add(f[i+1][(s0+k)%3][k][p|(j==k)],f[i][s0][j][p]); LL ans=check1()?0:1; int sum=fsum(); for(int i=0;i<3;i++) ans=(ans+f[n][sum][i][1])%mod; printf("%lld",ans); } int main(){ freopen("game.in","r",stdin); freopen("game.out","w",stdout); init(); if(check0()) printf("1"); else if(n<=3) sol1(); else sol2(); return 0; }
小结:
又是一道结论题。
当天总结:
前一天晚上选好了机子,结果当天一早来发现有人,心态$--$。
麻烦重重地再选了一台机子,发现忘记GMOJ密码,心态$--$。
小机房窗帘坏了,阳光很大,刺眼睛,心态$--$。
T1刚了将近两个小时忽然发现搞错题意,心态$--$。
转而写T2发现暴力没分,心态$--$。
赶时间码出T3暴力,心态$++$。
开始对T1找规律。其实结束之前已经搞出$n=6$的答案,然而顺着小规律的蛛丝马迹,没有抓住那一点灵感。感慨错亿。
心态调控还可以加强。
思维再$Sharpen$,找到更多更好的灵感,同时不让它溜走。