HZNU Training 29 for Zhejiang Provincial Competition 2020

L:

题意:给定一个n*m的网格,每个初始的网格有一个高度 ,每个a[ i ] 在[ l , r ]的区间里;
两种操作:
1,相邻方块高度+1
2 . 一个方块的高度+2
求使得若干次操作之后网格所有点到达同一高度的初始网格数量。

解法:考虑最初的网格有奇数有偶数,通过操作2可以把同为奇数的方格对齐,同为偶数的方格对齐,
最后剩下的网格必然是若干个高度为奇数,若干个高度为偶数的。模2处理后就变成了一个01矩阵。
问题转化为一个01矩阵对齐的问题。
如果1的点为偶数,那么通过操作1一定可以调平衡,0同理

讨论:

总方案数为:
$$
(l-r+1)^{n*m}
$$
n*m为奇数:

奇数 = 偶数 + 奇数,0,1矩阵里必然有为偶数的点,把偶数按操作1对齐即可。

答案 = 总方案数。

n*m为偶数:

偶数 = 偶数 + 偶数 : 可以调平衡,0向1调整,1向0调整都行。

偶数 = 奇数 + 奇数 :无法调平衡 。

答案为 总方案数 - 全为 奇数的情况。

即:
$$
令 x=[l,r]区间里奇数个数,y=[l,r]区间里偶数个数 \\所求答案即为:\\\sum_{i=0}^{n*m}C_{n*m}^ix^i *C_{n*m}^{n*m-i}y^i\:\:\:\:\:\:\:\:\:i为偶数\\=(x+y)^{n*m}\:\:\:\:\:的二项式展开中偶数项\\为得到(x+y)^{n*m}中偶数项:\\构造二项式:\\(x-y)^{n*m}=\sum_{i=0}^{n*m}(-1)^{n*m-i}C_{n*m}^ix^i *C_{n*m}^{n*m-i}y^i\\当i为奇数时,(x-y)^{n*m}和(x+y)^{n*m}的项相互抵消。\\即答案为:\frac{(x+y)^{n*m}+(x-y)^{n*m}}{2}\\以上:n*m为奇数:(x+y)^{n*m}\\n*m为偶数:\frac{(x+y)^{n*m}+(x-y)^{n*m}}{2}\\
$$

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const ll MOD=998244353;
ll quickPower(ll a, ll b,ll m) {   //计算a的b次方
    ll ans = 1;
    ll base = a;
    while (b) {
        if (b & 1) {
            ans *= base;
            ans %= m;
        }
        base *= base;
        base %= m;
        b >>= 1;   //注意是b>>=1 not b>>1
    }
    return ans;
}
int main(){
        ll x,y,l,r,n,m;
        scanf("%lld %lld %lld %lld",&n,&m,&l,&r);
        ll t=n*m,d=r-l+1;
        ll ans;
        ll nv2=quickPower(2,MOD-2,MOD)%MOD;
        if(d%2==0)x=y=d/2;
        else if(d%2==1&&l%2==1){
            x=d/2+1;
            y=d/2;
        }
        else if(d%2==1&&l%2==0){
            x=d/2;
            y=d/2+1;
        }
        ll sum=quickPower(d,t,MOD)%MOD;
        if(d==1)ans=1;
        else if(t%2==1)ans=sum;
        else  ans=(quickPower(x+y,t,MOD)%MOD+quickPower(x-y,t,MOD)%MOD)*nv2%MOD;
        ans%=MOD;
        printf("%lld\n",ans);

    // system("pause");
    return 0;
}
View Code

B:

题意:每个点可以控制与之相邻的点,求选取最小的点,使得所有点覆盖。

设dp[ i ] [ 0 ] 表示i被父结点覆盖

dp[ i ] [ 1 ] 表示 i 被自己覆盖

dp[ i ] [ 2 ] 表示 i 被儿子覆盖

转移即可。

// #include<bits/stdc++.h>
#include<cstdio>
#include<vector>
using namespace std;
#define pb push_back
typedef long long ll;
const int inf=1e8;
const int N=1e5+5;
vector<int>e[N];
int dp[N][3];
int n,ans;
void dfs(int u,int fa){
    bool flag=0;;int inc=inf;
    dp[u][1]=1;
    for(int i=0;i<e[u].size();i++){
        int v=e[u][i];
        if(v==fa)continue;
        dfs(v,u);
        dp[u][0]+=min(dp[v][1],dp[v][2]);
        dp[u][1]+=min(dp[v][0],min(dp[v][1],dp[v][2]));
         if(dp[v][1]<=dp[v][2]){
            flag=1;
            dp[u][2]+=dp[v][1];
        }
        else {
            dp[u][2]+=dp[v][2];
            inc=min(inc,dp[v][1]-dp[v][2]);
        }
    }
    if(!flag)dp[u][2]+=inc;
}
int main(){
    scanf("%d",&n);
    for(int i=1,u,v;i<n;i++){
        scanf("%d %d",&u,&v);
        e[u].pb(v);e[v].pb(u);
    } 
    dfs(1,-1);
    int ans=min(dp[1][1],dp[1][2]);
    printf("%d\n",ans);
    // system("pause");
    return 0;
}
View Code

 

E - 往

 UVA - 10766 

简单生成树计数。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstdio>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=3e2+100;
//ll mod=1;
ll A[maxn][maxn];
ll B[maxn][maxn];
ll determinant(int n){
    ll res=1;
    for(int i=1;i<=n;i++){
        if(!B[i][i]){
            bool flag=false;
            for(int j=i+1;j<=n;j++){
                if(B[j][i]){
                    flag = true;
                    for(int k=i;k<=n;k++){
                        swap(B[i][k],B[j][k]);
                    }
                    res=-res;
                    break;
                }
            }
            if(! flag)
            return 0;
        }
        
        for(int j=i+1;j<=n;j ++){
            while(B[j][i]){
                ll t=B[i][i]/B[j][i];
                for(int k=i;k<=n;k ++){
                    B[i][k]=B[i][k]-t*B[j][k];
                    swap(B[i][k],B[j][k]);
                }
                res=-res;
            }
        }
        res*= B[i][i];
    }
    return res;
}
int main(){
    int n, m, k;
    while(~ scanf("%d %d %d", &n, &m, &k)){   
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        for(int i=1;i<=m;i ++){
            int a,b;
            scanf("%d %d",&a,&b);
            A[a][b]=A[b][a]=1;
        }
        for(int i=1;i<=n;i ++){
            for(int j=1;j<=n;j++){
                if(i!=j&&!A[i][j]){
                    B[i][i]++;
                    B[i][j]=-1; 
                }
            }
        }
        n=n-1;
        ll ans=determinant(n); 
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

F - 后

 CodeForces - 557D 

给定一张图,求最少加多少个边,使得图里有奇环,输出方案总数。

解法:奇环必然与二分图染色有关。

加边为0:原本有奇环。

加边为1:选取一个联通块,点数>2,将同色染在一起。

加边为2:图里全为线段。

加边为3:没有边。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int N=1e5+5;
vector<int>e[N];
ll n,m;
ll white[N],black[N];
int color[N],belong[N],num[N];
bool vis[N];
bool dfs(int u,int scc){
    num[scc]++;
    belong[u]=scc;vis[u]=1;
    for(int i=0;i<e[u].size();i++){
        int v=e[u][i];
        if(color[v]==color[u]){return 0;}
        if(!color[v]){
            color[v]=3-color[u];
            if(!dfs(v,scc))return 0;
        }
    }
    return 1;
}
void init(){
    // memset(vis,0,sizeof vis);
    // memset(color,0,sizeof color);
    for(int i=1;i<=n;i++)
    vis[i]=color[i]=black[i]=white[i]=belong[i]=0;
}
int main(){
    scanf("%lld %lld",&n,&m);
    init();
    for(int i=1,u,v;i<=m;i++){
        scanf("%d %d",&u,&v);
        e[u].pb(v);e[v].pb(u);
    }
    if(m==0){ll ans=1ll*n*(n-1)*(n-2)/6;printf("3 %lld\n",ans);return 0;}
    int SCC=0;
    bool odd=false;
    for(int i=1;i<=n;i++){
        if(vis[i])continue;
        color[i]=1;
        if(dfs(i,++SCC)==0){
            odd=1;
            break;
        }
    }
    // for(int i=1;i<=n;i++)cout<<color[i]<<endl;
    if(odd){printf("0 1\n");return 0;}
    for(int i=1;i<=n;i++){
        if(color[i]==1)white[belong[i]]++;
        else  black[belong[i]]++;
    }
    int cnt=0;
    ll ans=0;
    for(int i=1;i<=SCC;i++){
        if(num[i]<=2){
            cnt++;
            continue;
        }
        ans+=(white[i]-1)*white[i]*1ll/2+(black[i]-1)*black[i]*1ll/2;
    }

    if(cnt==SCC){
        printf("2 %lld\n",m*(n-2)*1ll);
    }
    else printf("1 %lld\n",ans);
    // system("pause");
    return 0;
}
View Code

 

posted @ 2020-05-06 23:14  无声-黑白  阅读(130)  评论(0编辑  收藏  举报