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; }
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; }
E - 往
简单生成树计数。
#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; }
F - 后
给定一张图,求最少加多少个边,使得图里有奇环,输出方案总数。
解法:奇环必然与二分图染色有关。
加边为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; }