2025多校冲刺省选模拟赛7
\(T1\) A. 三色卡(card) \(0pts\)
-
如果存在一个小矩形和大矩形的大小相同,此时另外两个矩形可以任意放,贡献是容易计算的。
-
否则至少需要一个小矩形覆盖大矩形的两个角,通过交换长、宽钦定完全覆盖行的矩形比完全覆盖列的矩形的数量多。
-
完全覆盖行的矩形数量为 \(1,2\) 时判掉无解后是容易统计贡献的。
-
完全覆盖行的矩形数量为 \(3\) 时,直接在两边固定矩形后算另一个矩形可放置的极长区间会算重,不妨直接钦定不能顶到两边的边界,而顶到两边的边界单独提出来计算即可。
点击查看代码
const ll p=1000000007; ll h[5],w[5],a[5]; ll solve(ll a,ll b,ll c,ll m) {ll flag=(max(w[a],w[b])+w[c]>=m);//顶到两边的边界if(w[a]+w[b]>=m) return flag+m-w[c]-1;return flag+min(m-w[c],w[a]+1)-max(2ll,m-w[b]-w[c]+1)+1; } int main() { #define Isaac #ifdef Isaacfreopen("card.in","r",stdin);freopen("card.out","w",stdout); #endifll t,n,m,cnt,s1,s2,sum,minn,ans,i,j;cin>>t;for(j=1;j<=t;j++){cin>>n>>m;cnt=s1=s2=sum=ans=0; minn=0x7f7f7f7f;for(i=1;i<=3;i++){cin>>h[i]>>w[i]; a[i]=i;cnt+=(h[i]==n&&w[i]==m);s1+=(h[i]==n); s2+=(w[i]==m);}if(s2>s1){swap(s1,s2); swap(n,m);for(i=1;i<=3;i++) swap(h[i],w[i]);}if(cnt==0){if(s1==1){for(i=1;i<=3;i++){if(h[i]==n) s2=w[i];else{sum+=h[i];minn=min(minn,w[i]);}}ans=(minn+s2>=m&&sum>=n)*4;}if(s1==2){ans=1;for(i=1;i<=3;i++){if(h[i]==n) sum+=w[i];else ans=ans*(n-h[i]+1)%p*(m-w[i]+1)%p;}ans=(sum>=m)?2*ans:0;}if(s1==3){for(i=1;i<=3;i++) sum+=w[i];if(sum>=m){do{ans=(ans+solve(a[1],a[2],a[3],m))%p;}while(next_permutation(a+1,a+1+3));}}}else{ans=1;for(i=1;i<=3;i++) ans=ans*(n-h[i]+1)%p*(m-w[i]+1)%p;}cout<<ans<<endl;}return 0; }
\(T2\) B. 三项式(sequence) \(20pts\)
-
部分分
-
\(20pts\) :爆搜。
点击查看代码
const ll p=1000000007; int ans=0; bool check(ll sum,int m) {int s1=0,s2=0;while(sum){s1=(s1+sum%10)%m;s2=(s2+(sum%10)*(sum%10))%m;sum/=10;}return (s1==s2); } void dfs(int pos,int n,int m,ll sum,int l,int r) {if(pos==n+1){if(check(sum,m)==true) ans=(ans+1)%p;return;}for(int i=l;i<=r;i++) dfs(pos+1,n,m,sum+i,l,r); } int main() { #define Isaac #ifdef Isaacfreopen("sequencel.in","r",stdin);freopen("sequencel.out","w",stdout); #endifint n,m,l,r,lens,lene,i;cin>>n>>m>>l>>r;dfs(1,n,m,0,l,r);cout<<ans<<endl;return 0; }
-
-
正解
\(T3\) C. 三分图(graph) \(0pts\)
-
单看限制条件 \(3\) 太误导人了,等价于在限制 \(1\) 成立的条件下不存在两端属于同个部分的边。
-
考虑扔到 \(DFS\) 树上考虑,从而天然满足第二条限制。
-
将所有点按照深度的奇偶性划分成两个部分 \(V_{1},V_{2}\) ,然后进行分讨。
- 若各部分点数均 \(\le 2n\) ,选择符合第一条限制。由树上完美匹配经典结论贪心选择即可。
- 否则,选择符合第三条限制。具体地,观察到叶子节点构成了独立集且足够多(根据 \( ||V_{1}|-|V_{2}|| \ge n\) 可知),将所有叶子节点扔到 \(V_{3}\) 里即可。
点击查看代码
struct node {int nxt,to; }e[1000010]; int head[1000010],dep[1000010],vis[1000010],son[1000010],ins[1000010],ans[1000010],num[2],cnt=0,n; void add(int u,int v) {cnt++;e[cnt].nxt=head[u];e[cnt].to=v;head[u]=cnt; } void dfs1(int x,int fa) {vis[x]=1;dep[x]=dep[fa]+1;num[dep[x]%2]++;ans[x]=dep[x]%2+1;for(int i=head[x];i!=0;i=e[i].nxt){if(vis[e[i].to]==0) {son[x]++;dfs1(e[i].to,x);}} } void dfs2(int x) { int flag=0;vis[x]=1;for(int i=head[x];i!=0;i=e[i].nxt){if(vis[e[i].to]==0){dfs2(e[i].to);flag|=ins[e[i].to];}}if(flag==0&&num[dep[x]%2]-1>=n){num[dep[x]%2]--;ans[x]=3; ins[x]=1;} } int main() { #define Isaac #ifdef Isaacfreopen("graph.in","r",stdin);freopen("graph.out","w",stdout); #endifint t,m,u,v,i,j;scanf("%d",&t);for(j=1;j<=t;j++){scanf("%d%d",&n,&m);fill(e+1,e+1+cnt,(node){0,0});fill(head+1,head+1+3*n,0);fill(vis+1,vis+1+3*n,0);fill(son+1,son+1+3*n,0);fill(ins+1,ins+1+3*n,0);cnt=num[0]=num[1]=0;for(i=1;i<=m;i++){scanf("%d%d",&u,&v);add(u,v); add(v,u);}dfs1(1,0);if(max(num[0],num[1])<=2*n){fill(vis+1,vis+1+3*n,0);dfs2(1);}else{for(i=1;i<=3*n;i++) ans[i]=((son[i]==0)?3:ans[i]);}printf("YES\n");for(i=1;i<=3*n;i++) printf("%d ",ans[i]);printf("\n");}return 0; }
总结
- \(T1\) 分讨完后觉得太难写就直接摆烂去写左偏树了。而且当时对于三个同时覆盖行的情况算重了贡献,如果在场上估计也想不到解决方案。
- \(T2\) 一开始读假题了,口胡了个 \(O(\dbinom{n+m-1}{m-1})\) 的做法。
后记
- \(T2\) accoders NOI 和学校 \(OJ\) 的文件 IO 不一样。
- \(T3\) 原
checker.cpp
的并查集没有初始化导致循环输出 \(1,2,3\) 即可通过本题。