纪中集训2019.11.05

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;

}
View Code

小结:

  精准分配的时候,可以运用这种“关联特征量”的思路。比如:电影院放映厅按照单双号将人群分流到两个大门,人们按照行号寻找座位。

  一句题外话:下午讲题(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;

}
View Code

小结:

  实在不行,打表找规律√

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;

}
View Code

小结:

  又是一道结论题。

当天总结:

  前一天晚上选好了机子,结果当天一早来发现有人,心态$--$。

  麻烦重重地再选了一台机子,发现忘记GMOJ密码,心态$--$。

  小机房窗帘坏了,阳光很大,刺眼睛,心态$--$。

  T1刚了将近两个小时忽然发现搞错题意,心态$--$。

  转而写T2发现暴力没分,心态$--$。

  赶时间码出T3暴力,心态$++$。

  开始对T1找规律。其实结束之前已经搞出$n=6$的答案,然而顺着小规律的蛛丝马迹,没有抓住那一点灵感。感慨错亿。

  心态调控还可以加强。

  思维再$Sharpen$,找到更多更好的灵感,同时不让它溜走。

原文地址:https://www.cnblogs.com/Hansue/p/11799179.html