gwyh 测试赛 验题人 - 题解 (非std做法)

测试赛 - ljc20020730 解题报告

标签(空格分隔): solution


Task A Tiat's easy question

首先,判断图中是否存在长度为奇数的环等价于判断图是否为二分图。
这个两个事情互为充分必要条件。

只需要染色即可,图可能不连通。

复杂度\(O(n)\)

# include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=2e6+10;
struct rec{
    int pre,to;
}a[M<<1];
int n,m,tot,head[N],col[N];
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs(int u,int c)
{
    col[u]=c;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; int sd=(c==1)?2:1;
        if (col[v]==col[u]) { puts("N"); exit(0);}
        if (!col[v]) dfs(v,sd);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++) {
        int u,v; scanf("%d%d",&u,&v);
        adde(u,v); adde(v,u);
    } 
    for (int i=1;i<=n;i++) if (!col[i]) dfs(i,1);
    puts("Y");
    return 0;
} 

Task B Nephren's last plan

我们观察到,完全平方数完全是可以取或者不取的,可以统一起来考虑。

不妨设完全平方数的个数为\(ret_1\)个.

而对于其他非完全平方数,我们可以对其进行完全分解。

\(a_i\)完全分解中某一项为\(p_i ^ {k_i}\) 其中$p_i \in Prime $

  • \(k_i \equiv 0 (mod\ 2)\) , 则 该元素对 质数 \(p_i\) 没有意义。

  • \(k_i \equiv 1 (mod\ 2)\) , 则 该元素对 质数 \(p_i\) 有意义。

由于最终必须是连乘使得最终答案是完全平方数,这等价于对每个素数有贡献的元素中只能选择偶数个。 对不同素数有贡献的元素之间又存在连带关系。

所以,对于每一个素数都可以对应一个异或方程组,我们可以将其写成矩阵的形式。

定义矩阵\(A_{i,j}\)表示下列方程组的解 \((x_1 , ... , x_n)\)

$A_{1,1} \times x_1 \bigoplus A_{1,2} \times x_2 \bigoplus...\bigoplus A_{1,n} \times x_n = 0 $
$A_{2,1} \times x_1 \bigoplus A_{2,2} \times x_2 \bigoplus...\bigoplus A_{2,n} \times x_n = 0 $
\(\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ......\)
$A_{n,1} \times x_1 \bigoplus A_{n,2} \times x_2 \bigoplus...\bigoplus A_{n,n} \times x_n = 0 $

可以高斯消元求出矩阵的秩 , 然后求出自由元个数\(ret_2\)

最终的答案\(\ ans \\)一定是\(2^{ret_1+ret_2} \equiv ans \ (mod\ 998244353)\)

注意到值相同的不同元素应该算为不同的元。

# include<bits/stdc++.h>
# define int long long
using namespace std;
bool ok[505],is_pr[505];
int n,cnt,ret1,ret2,num;
int a[1005],t[505][505],pr[505],mark[505];
vector<int>p[505];
void EouLaShai(int Lim)
{
    memset(is_pr,true,sizeof(is_pr));
    is_pr[1]=false;
    for (int i=2;i<=Lim;i++) {
        if (is_pr[i]) pr[++pr[0]]=i,mark[i]=i;
        for (int j=1;j<=pr[0]&&i*pr[j]<=Lim;j++) {
            mark[i*pr[j]]=pr[j];
            is_pr[i*pr[j]]=false;
            if (i%pr[j]==0) break;
        }
    }
}
void work(int x)
{
    ++num; bool flag=false;
    while (x!=1) {
        int y=mark[x],z=0; 
        while (x%y==0) x/=y,z++;
        if (z&1) p[y].push_back(num),flag=true;
    }
    if (!flag) --num;
}
int gauss()
{
    int i,j,k,x,y;
    for (i=1,j=1;i<=cnt&&j<=num;j++) {
        k=i; while (k<=cnt&&!t[k][j]) ++k;
        if (t[k][j]) {
            swap(t[i],t[k]);
            for (x=i+1;x<=cnt;x++) if (t[x][j])
             for (y=i;y<=num;y++) t[x][y]^=t[i][y];
            i++;
        }
    }
    return num-i+1;
}
int Pow(int x,int n,int p)
{
    int ans=1;
    while (n) {
        if (n&1) ans=ans*x%p;
        x=x*x%p; n>>=1;
    }
    return ans%p;
}
signed main()
{
    EouLaShai(500); scanf("%lld",&n);   
    for (int i=1;i<=n;i++) {
        int t; scanf("%lld",&t);
        if (((int)sqrt(t))*((int)sqrt(t))==t) { ret1++; continue;}
        a[++a[0]]=t;
    }
    for (int i=1;i<=a[0];i++) work(a[i]);
    for (int i=1;i<=500;i++) if (is_pr[i] && p[i].size()) {
        ++cnt;
        for (int j=0;j<p[i].size();j++) t[cnt][p[i][j]]=1;
    }
    int ret2=gauss();
    printf("%lld\n",Pow(2,ret1+ret2,998244353));
    return 0;
}

Task C Tiat and random

首先,黑点=白点的情况对答案没有贡献,可以不用考虑。
设染黑点数目少于白点数目,显然另外一半情况与该情况完全相同。

设染黑点数目是\(i\),为了满足上述限制,\(i \in [0,\lfloor\frac{k}{2} \rfloor ]\)

那么选取的方法是\(\binom{k}{i}\)种,总贡献是\(\binom{k}{i}(k-2i)\)

所以这道题最终答案就是\(\frac{2 \sum\limits _{i=0} ^{\lfloor\frac{k}{2} \rfloor}\binom{k}{i}(k-2i)}{2^k}\)

\((2^{-1})^{k-1} \sum\limits _{i=0} ^{\lfloor\frac{k}{2} \rfloor}\binom{k}{i}(k-2i) \ mod \ 2000003\)

由于有\(n\)组询问,这样计算时间复杂度是\(O(nk)\)
我们考虑转化一下这个式子。

我们关心 $\sum\limits _{i=0} ^{\lfloor\frac{k}{2} \rfloor}\binom{k}{i}(k-2i) $ 这一部分。

原式可化为 : \(k \sum\limits_{i=0}^{\lfloor\frac{k}{2} \rfloor}\binom{k}{i} - 2\sum\limits_{i=0}^{\lfloor\frac{k}{2} \rfloor} i\binom{k}{i}\)

显然若\(k = 2k'\)\(k=2k'+1,k'\in Z\),需要讨论。

观察\(\sum\limits_{i=0}^{\lfloor\frac{k}{2} \rfloor}\binom{k}{i}\) 我们可以归纳得出,

  • \(k=2k'\)时,值就是$2^{k-1} + \frac{1}{2} \binom{k}{k'} $
  • \(k=2k'+1\)时,值就是\(2^{k-1}\).

观察\(\sum\limits_{i=0}^{\lfloor\frac{k}{2} \rfloor} i\binom{k}{i} = \sum\limits_{i=1}^{\lfloor\frac{k}{2} \rfloor} i\binom{k}{i}\),而可以证明:$i\binom{k}{i} = k \binom{k-1}{i-1} $

这里证明一下结论 \(i\binom{k}{i} = k \binom{k-1}{i-1}\) : $i\binom{k}{i} =\frac{i\times k!}{(k-i)!i!} = \frac{k\times(k-1)!}{(k-i)!(i-1)!} = k\binom{k-1}{i-1} $

\(\sum\limits_{i=0}^{\lfloor\frac{k}{2} \rfloor} i\binom{k}{i}\)可化为\(k \sum\limits_{i=1}^{\lfloor\frac{k}{2} \rfloor} \binom{k-1}{i-1} = k \sum\limits_{i=0}^{\lfloor\frac{k}{2} \rfloor -1} \binom{k-1}{i}\)

我们可以归纳得出:

  • \(k=2k'\)时,答案就是\(2^{k-2}k\),
  • \(k=2k'+1\)时,答案就是\(\frac{2^{k-1}-\binom{k-1}{k'}}{2} k\).

由于真正的答案 $ Ans = (2^{-1})^{k-1} (k \sum\limits_{i=0}^{\lfloor\frac{k}{2} \rfloor}\binom{k}{i} - 2\sum\limits_{i=0}^{\lfloor\frac{k}{2} \rfloor} i\binom{k}{i})$

  • 若当\(k=2k'\)时,
    \(Ans = (2^{-1})^{k-1} \times (k(2^{k-1} + \frac{1}{2} \binom{k}{k'})-2(k\times 2^{k-2}))\)
    \(= (2^{-1})^k k \binom{k}{k'} = 2k'\binom{2k'}{k'}(2^{-1})^{2k'}\)
  • 若当\(k=2k'+1\)时,
    $Ans = (2^{-1})^{k-1} \times (k(2^{k-1})-2\times \frac{2^{k-1}-\binom{k-1}{k'}}{2} k ) $
    \(= \binom{k-1}{k'} k (2^{-1}) ^{k-1} =(2k'+1) \binom{2k'-1}{k'} (2^{-1}) ^{2k'-1}\)

综上所述,(\(k\)为奇数且为1时,组合数会取到负数,我们可以特判处理)
\(Ans(1) = \frac{|1-0|+|0-1|}{2^1} = 1.\)
\(Ans(2k') = 2k'\binom{2k'}{k'}(2^{-1})^{2k'} , k' \in N\).
\(Ans(2k'+1) = (2k'+1) \binom{2k'-1}{k'} (2^{-1}) ^{2k'-1},k'\in N^+\).

实现方面,可以预处理逆元、阶乘前缀积、阶乘逆元前缀积、2的逆元幂次等然后通过lucas定理求出组合数取模,然后由于模数不是非常小,所以询问基本上是\(O(1)\)的。
总复杂度: \(O(n)\)

# include<bits/stdc++.h> 
# define int long long
using namespace std;
const int N=2e6+1000;
const int mo=2000003;
int s[N],inv[N],Pow[N];
int n,m;
void init(int len)
{
    s[0]=inv[0]=inv[1]=1;
    for (int i=1;i<=len;i++) s[i]=s[i-1]*i%mo;
    for (int i=2;i<=len;i++) inv[i]=(mo-mo/i)%mo*inv[mo%i]%mo;
    for (int i=2;i<=len;i++) inv[i]=inv[i-1]*inv[i]%mo;
    Pow[0]=1; for (int i=1;i<=len;i++) Pow[i]=Pow[i-1]*inv[2]%mo;
}
int lucas(int n,int m)
{
    if (m>n) return 0;
    if (n<mo&&m<mo) return s[n]*inv[n-m]%mo*inv[m]%mo;
    return lucas(n%mo,m%mo)*lucas(n/mo,m/mo)%mo;
}
signed main(){
    init(2e6+100); 
    int n; scanf("%lld",&n);
    while (n--) {
        int k; scanf("%lld",&k);
        if (k==1) { puts("1");continue;}
        if (k&1) {
            k/=2; int ans=(2*k+1)*Pow[2*k-1]%mo*lucas(2*k-1,k)%mo;
            printf("%lld\n",ans);
        } else {
            k/=2; int ans=(2*k)*Pow[2*k]%mo*lucas(2*k,k)%mo;
            printf("%lld\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ljc20020730/p/11291482.html