USACO P组题解

[USACO22FEB]铂金

\(\large {题解顺序根据主观难度排序}\)

T2:Sleeping in Class P

暴力部分

其实是整活部分

subtask1

第一档巨大的部分分是给朴素暴力做法的
只需要想出贪心策略即可
这题的策略不难从样例中看出
分成 \(a_i\) 大于\(q\)\(a_i\) 小于\(q\)的情况
大于\(q\)的对答案的贡献是拆 \(\frac{a_i}{q}\) 次 有余数再分出去 合并共两次
小于\(q\)直接分出去贡献一次
做完如上即可获得60+pts
10min即可解决
接着可以想到优化掉几个计算的过程 减小常数
考虑将所有数全部合并再分裂;
能够整除 q 的前缀和对应的切割点处不用分裂再合并;
这样实际中可以减少两步。
推得式子:
\(ans=\frac{pre_n}{q}+n-2\sum[pre_i\bmod q=0]\)
\(pre_i表示i位前缀和\)
可以获得更高的分数70+pts

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,Q;
ll a[100001],pre[100001],q;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i)cin>>a[i],pre[i]=pre[i-1]+a[i];
	cin>>Q;
	while(Q--){
		cin>>q;
		if(pre[n]%q){cout<<"-1\n";continue;}
		ll ans=pre[n]/q+n,t=0;
		for(int i=1;i<=n;i++)t+=!(pre[i]%q);
		ans-=2*t;
		cout<<ans<<'\n';
	}
}

subtask2

接着是一个神秘优化
特殊数据都告诉你了q相同
于是使用哈希表进行判重
前面的暴力写的好的话这里加个判重可以再多点分

//这是个buff叠满了的暴力
//把能想到的优化全加上了
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
#define ll long long
#define re register
int n,Q;
ll a[100001],pre[100001],p;
ll rd(){
    ll x=0,f=1;
    char c=getchar();
    while (c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
__gnu_pbds::gp_hash_table<ll,ll>mp;
int main(){
	n=rd();
	for(re int i=1;i<=n;++i)a[i]=rd(),pre[i]=pre[i-1]+a[i];
	Q=rd();
	while(Q--){
		p=rd();
		if(mp[p]){cout<<mp[p]<<"\n";continue;}
		if(pre[n]%p){cout<<"-1\n";continue;}
		ll ans=pre[n]/p+n,t=0;
		for(re int i=1;i<=n;++i)t+=!(pre[i]%p);
		ans-=2*t;
		cout<<(mp[p]=ans)<<'\n';
	}
} 

这题虽然正解较为困难
但是部分分属于想了就有分的那种
本来部分分高达90
实在看不下去了直接小改了一番

正解部分

注意到整个暴力中只有下面这段有较大的复杂度
\(\sum[pre_i\bmod q=0]\)
于是我们可以从每个 \(pre_i\) 对答案的贡献入手
\(S=pre_i\)
考虑一个 \(S\) 的贡献
\(S\)对一个\(q\)有贡献当且仅当 \(q|S\)\(q|pre_n\)
所以显然 \(s_i\) 对所有 \(gcd(s_i,s_n)\)的因子均有贡献
接着设 \(\gcd(s_i, s_n) = \prod p_i ^ {c_i}\)
于是 \(S\) 对于每个 \(\prod\limits_{0\leq b_i \leq c_i} p_i ^ {b_i}\)均有1的贡献
我们把每个质因数的指数看作一条空间上的数轴,则 \(\gcd(s_i,s_n)\) 就是 \(k\) 维空间上的一个点 \((a_1,a_2,\cdots,a_k)\)
由于\(q\) 质因数分解的每个指数 \(b_i\) 都满足 \(0 \leq b_i \leq a_i\)
那贡献的对象就是超立方体 \((0,0,\cdots,0) \sim (a_1,a_2,\cdots,a_k)\) 中的点了

此处复杂度为
\(n\log (s_n) + d(s_n)\omega(s_n)\log d(s_n)\)
最后一个\(log\)可以通过使用哈希表而不是\(map\)来优化掉
所以在顶点那里差分一下再高维后缀和就好了
接着下面就是质因数分解的部分
$ Pollard - rho$ 是较多数人的选择
但是由于码量过长或者干脆就是不会的原因
$ Pollard - rho$ 读者自学应该不难我就不贴代码了
这里看到了一种好写但是速度会慢一些的做法 推荐给各位
考虑对于一个数只分解到\(10^6\)
即使用\(10^6\)内—的数试除\(pre_n\)
如果结果小于\(10^{12}\)那么剩下的就也是个质数
这是由于剩下的数如果是合数必然有小于\(10^6\)的质因子
而所有小于\(10^6\)的数都试除完了所以剩下的数必然是质数
如果剩下的数大于\(10^{12}\)那么直接暴力做即可
总复杂度为
\(n\max\limits_{i \leq 10 ^ 6} d(i) + n\log (s_n) + d(s_n)\omega(s_n)\log d(s_n)\)

#include<bits/stdc++.h>
#define int long long//懒得判断开不开了一律都开
const int N=200001;
using namespace std;
int n,q,a[N];
int pr[N],cnt,c[N],w[N],p[N];
map<int,int>mp,f;
inline int qpow(int a,int b){//快速幂
    int r=1;
    while(b){if(b&1)r*=a;a*=a,b>>=1;}
    return r;
}
inline int ntp(int *uc){//把数字转化为坐标
    int r=0;
    for(int i=1;i<=cnt;++i)r+=uc[i]*w[i];
    return r;
}
inline int ptn(int x){//反过来
    int r=1;
    for(int i=1;i<=cnt;++i){
        int cnum=x/w[i]%(c[i]+1);
        r*=qpow(pr[i],cnum);
    }
    return r;
}
inline void presum(int wd,int ch){//高维前缀和
    if(wd>cnt){
        int x=ntp(p);
        f[x-w[ch]]+=f[x];
        return;
    }
    for(int i=c[wd];i>=(wd==ch);i--)p[wd]=i,presum(wd+1,ch);
}
inline void init(){//质因数分解S_n
    int mx=a[n];
    for(int i=2;i<=1e6;++i)
        if(!(mx%i)){
            pr[++cnt]=i;
            while(!(mx%i))c[cnt]++,mx/=i;
        }
    if(mx<=1e12&&mx>1)pr[++cnt]=mx,c[cnt]++;
    else if(mx!=1)return;
    w[cnt]=1;
    for(int i=cnt-1;i>=0;i--)w[i]=w[i+1]*(c[i+1]+1);//计算维度单位
    for(int i=1;i<n;++i){//记录点gcd(S_i,S_n)
        int tmp = a[i];
        for(int j=1;j<=cnt;j++){
            int cur=0;
            while(!(tmp%pr[j]))cur++,tmp/=pr[j];
            p[j]=min(cur,c[j]);
        }
        f[ntp(p)]++;
    }
    for(int i=1;i<=cnt;++i)presum(1,i);//逐维高维前缀和
    for(int i=0;i<w[0];++i)mp[ptn(i)]=(n-1)+(a[n]/ptn(i)-1)-2*f[i];//枚举点与记录ans
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i],a[i]+=a[i-1];//a_i已无用直接放弃
    init(),cin>>q;
    while(q--){
        int qq;
        cin>>qq;
        if(a[n]%qq)cout<<"-1\n";
        else{
            if(!mp[qq]){
                int r=0;
                for(int i=1;i<n;++i)r+=!(a[i]%qq);
                mp[qq]=(n-1)+(a[n]/qq-1)-2*r;
            }
            cout<<mp[qq]<<'\n';
        }
    }
    return 0;
}

T3:Phone Numbers P

正解部分

部分分可以尝试可以面向数据编程,这里不做多介绍
只包含5,6,8,9的数据就可以乱搞,只包含1,2,3的也可以试试
那么正解我们可以很快的想到的dp
但是我们需要怎么进行dp呢
首先可以想到如果给定了一个输入序列s,再给一个序列t
让你判定t能否从s中生成
显然这就是一个dp
\(dp_i\)可以从\(dp_{i-1},dp_{i-2},dp_{i-3}\)转移而来
然后你会发现你可以再套一个dp,以前面的dp值作为状态
\(f_{i,a,b,c,dp_{i-3},dp_{i-2},dp_{i-1},dp_{i}}\)即当前遍历到第i位,i位是c,i-1位是b,i-2位是a的答案
则确定了 \(t_i+1\) 之后就有唯一的下一个状态。
只需要枚举第\(i+1\)位进行转移即可,转移过程显然请读者自行思考
这样子复杂度是\(O(n)\)的,不过常数较大,有\(9^3*2^3\)
复杂度上可能无法再做优化,我们只能从常数入手
可以发现有一些显然不合法的比如状态全部都是0这一类的可以直接略去
考虑优化。首先容易发现若 \(dp_{i-3}=0\),那么 \(a\)就没用了,就不用记录了。
\(dp_{i-3}=dp_{i-2}=0\),那么\(b\)也就没用了。
\(dp_{i-3}​=dp_{i-2}=dp_{i-1}=0\),那么\(c\)也就没用了
\(dp_{i-3}=dp_{i-2}=dp_{i-1}=dp_i=0\),那么这个状态不合法。但是 $dp_{i-3}=1 $的时候还是要记 \(a,b,c\),状态数只减少了不到一半。
\(dp_{i-3}=1\),但是 \(a,b,c\)再加上任何一个数,都不能和 \([i-2,i+1]\)匹配上,那么可以把 \(dp_{i-3}\)改成 \(0\)。或者如果 \([i-2,i+1]\)
的数不在一个正方形内,也可以把 \(dp_{i-3}\)改成 \(0\)
同理 \(dp_{i-2},dp_{i-1}\)也可以这样做。
然后再压缩一下状态,具体的可以看代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
const int N=1e5+9,Mod=1e9+7;
int T,n,a[N];
char s[N];
bool chk2(array<int,2>a){
    sort(a.begin(),a.end());
    if(a[0]+3==a[1])return 1;
    if(a[0]+1==a[1]&&a[0]!=3&&a[0]!=6)return 1;
    return 0;
}
bool chk4(array<int,4>a){
    sort(a.begin(),a.end());
    if(a[0]==1&&a[1]==2&&a[2]==4&&a[3]==5)return 1;
    if(a[0]==2&&a[1]==3&&a[2]==5&&a[3]==6)return 1;
    if(a[0]==4&&a[1]==5&&a[2]==7&&a[3]==8)return 1;
    if(a[0]==5&&a[1]==6&&a[2]==8&&a[3]==9)return 1;
    return 0;
}
bool equ(vector<int>a,vector<int>b){sort(a.begin(),a.end()),sort(b.begin(),b.end());return a==b;}
void Upd(int&x,int y){
    x+=y;
    if(x>=Mod)x-=Mod;
}
int f[2][16005],cur;
vector<int>S;
bool vis[16005],ck2[N],ck4[N];
bool good1[10][10][10],good2[10][10];
int H(int x,int y,int z,int a,int b,int c,int d){return x*1600+y*160+z*16+a*8+b*4+c*2+d;}
void solve(){
    cin>>s+1,n=strlen(s+1);
    rep(i,1,n)a[i]=s[i]-'0';
    memset(f,0,sizeof(f)),memset(vis,0,sizeof(vis)),cur=0;
    f[0][1]=1,S.push_back(1);
    rep(i,0,n+1)ck2[i]=ck4[i]=0;
    rep(i,2,n)ck2[i]=chk2({a[i-1],a[i]});
    rep(i,4,n)ck4[i]=chk4({a[i-3],a[i-2],a[i-1],a[i]});
    rep(i,1,n){
        memset(f[cur^1],0,sizeof(f[cur^1]));
        vector<int>nS;
        for(int s:S)vis[s]=0;
        vector<int>trs;
        rep(j,max(1,i-3),min(n,i+3))trs.push_back(a[j]);
        sort(trs.begin(),trs.end());
        trs.resize(unique(trs.begin(),trs.end())-trs.begin());
        for(int s:S){
            int a3=(s>>4)/100,a2=(s>>4)/10%10,a1=(s>>4)%10,f3=(s>>3)&1,f2=(s>>2)&1,f1=(s>>1)&1,f0=s&1;
            int val=f[cur][s];
            for(int a0:trs){
                int nf=0;
                if(a[i]==a0)nf|=f0;
                if(!nf){
                    if(i>1&&ck2[i]&&equ({a[i-1],a[i]},{a1,a0}))nf|=f1;
                    if(!nf&&i>3&&ck4[i]&&equ({a[i-3],a[i-2],a[i-1],a[i]},{a3,a2,a1,a0}))nf|=f3;
                }
                if(f2+f1+f0+nf==0)continue;
                int na2=a2,na1=a1,na0=a0,nf2=f2,nf1=f1;
                if(!f2)na2=0;
                if(!f2&&!f1)na1=0;
                if(!f2&&!f1&&!f0)na0=0;
                if(!good1[na2][na1][na0])na2=0,nf2=0;
                if(!good2[na1][na0])na1=0,nf1=0;
                int nH=H(na2,na1,na0,nf2,nf1,f0,nf);
                if(!vis[nH])vis[nH]=1,nS.push_back(nH);
                Upd(f[cur^1][nH],val);
            }
            f[cur][s]=0;
        }
        S=nS,cur^=1;
    }
    int ans=0;
    for(int s:S)if(s&1)Upd(ans,f[cur][s]);
    cout<<ans<<'\n';
}
int main(){
    cin>>T;
    rep(i,0,9)rep(j,0,9)rep(k,0,9)rep(l,0,9)if(chk4({i,j,k,l}))good1[i][j][k]=good2[i][j]=1;
    while(T--)solve();
    return 0;
}

T1:Paint by Rectangles P

部分分部分

subtask1

按照官方题解的说法此部分分数据实际是一个二分图
我们考虑每次往图内加入一个矩形并翻转矩形内部颜色,发现符合题目要求(都有黑白染色方案了肯定是二分图
把离散化后的每个单位格子看成一个点,然后把没有矩形边界隔开的格子并查集连起来,然后跑二分图黑白染色即可

subtask2

"矩形不交"一个有用的信息
首先区域数就是n+1,每多一个矩形多一个区域,考虑黑色数。
考虑上面"每次往图内加入一个矩形并翻转矩形内部颜色"的办法,所以每个矩形的颜色由有多少个包含它的矩形决定
正解中是用了扫描线加二维偏序数一下矩形个数。

subtask3

这个部分分可以用欧拉定理然后算交点解决
我做题的时候并没有想到所以大致略过
下面这段我从别的题解粘来的,供各位参考:
以下引用来自官解和一些别的题解
真的懒得想了反正对正解的用处不大

由于连通了,考虑欧拉公式 F=E−V+C+1,此时连通块数C=1。
发现相交了就有交点,同时会把原来矩形的一条边割出两条来;
设交点个数为t,有:
V=4n+t
E==4n_2t
F=t+2

subtask4

从左往右考虑一根竖直的线,初始是白的,扫过的每个时刻显示其当前竖直位置每个区域的颜色
盗个图先)
每次遇到一个矩形的竖线,根据“每次往图内加入一个矩形并翻转矩形内部颜色”,我们进行黑白颜色翻转,考虑这次翻转的贡献。下面的区间指原来/现在的颜色段和当前翻转区间位置的两重限制下的极长区间。

如果原来这个区间是白色的变成了黑色:

不与现有的其他黑色相连,我们称出现了新的黑色线段,贡献为 +1。

与现有的其他黑色相连,由于每个位置只有一个矩形的竖线,也即我们只会翻转一个连续区间,这里现有的其他黑色必定是原来就有的黑色而我们没有动它。

只与现有的一个黑色段相连,我们称是原来黑色的延伸,贡献为 0。
与现有的两个黑色段相连,即左右都接了一个黑色段,我们称是原来黑色的合并,贡献为 −1.

如果原来这个区间是黑色的变成了白色:

且出现了原本的黑色区间的分裂,贡献为 0.
且出现了一整个黑色区间的消失,贡献为 0。

其实这不就没贡献不用考虑

以上引用自官解
这个算法发现在连通的情况下是正确的
而且这也是接下来正解的基础部分

subtask5

这段部分分的做法是由subtask3得来的
我写此题的时候比较着急直接略过了
大致就是把3的情况扩展到 \(C\ne1\) 的情况
于是我们要计数的只有
C,其他的 E,v_还是一样的。
只不过扫描线不是算交点个数,而是把每个位置的线段合并到一个并查集上。
写法是上线段树,打懒标记。
可能会假掉我没有实际测试这个做法
而且官解中也写得很不明不白就直接上来一句
To do this, we can use a segment tree.
我也会说:To do this,we can use c++

正解部分

我们重新来一遍扫描线
在扫描线扫的过程中
分成两种情况

\(\large 首先是碰到左边缘线的情况\)

先上几张图给大家看一下
可以通过这几张图推出左边缘作用
直接瞪眼大法



扫描到一个左边缘时,它的作用是对扫描线上这条边缘覆盖的范围的颜色取反
我们只统计答案,所以只要知道线扫过去之后改变了几块的颜色,就可以对应更新答案。

\(\large 接着是碰到右边缘线的情况\)

再上两张图

可以发现,在这两种情况中,扫描线经过一个右边缘的时候,原本左边最上面和最下面两块到右边就会和上方的异色块“合并”,而中间的其它块会反色且数量保持不变。最终答案应该增加中间的那些色块的数量。
但是刚刚说到“上下两块会‘消失’”,那么如果上下在同一块,是不是可以直接不处理?
实际上并不可以。考虑下图的情况,扫描线扫到一个右边缘后,上下两边的黑色块合并为了一块,导致之前计算为两块的黑色块实际上是一块,所以白色数量应该减一。

但是再看下图的情况,这里扫到右边缘时,又不应该把白色的数量减一。
因为它们在前面已经是连在一块的了,这里并没有被当成多块算。

所以只有当且仅当扫描线经过当前右边缘,且碰到特殊情况时,要将外面颜色的颜色数量 −1这种特殊情况当且仅当这个右边缘上下两块先前被当成了两块。不难发现,如果当前扫过的矩形和外面那块的矩形边界连通,才会导致外面的矩形的这两个部分在扫描线扫过里面这个矩形的右边缘之前一直被当做是两块(因为,如果中间不连通,断开了,那么在断开的那个时候上下两块就已经被连接在一起了,此时并没有被当成是不同的两块)。所以,在扫到右边缘且碰到这种特殊情况时,只需要判断当前右边缘所在的矩形与它外面(上方、下方、右侧)的那个矩形(如果有的话),边界是否连通即可。如果连通,再将对应颜色的答案-1
但实际上应该怎么确定外面的那个矩形的编号呢?实际上并不需要这样做,每次碰到右边缘的这种特殊情况就直接将外面的这个 −1 即可。减多了加回去就行。每次减多的时候,里面的这个矩形和外面的不连通,即这是另一个连通块。考虑一整个连通块,这个连通块外侧的颜色必然是相同的。假设这个连通块的某个右边缘减多。这个右边缘必然在最外侧,所以被减多的颜色应该是这整个连通块外面的颜色。显然,这个颜色在第一次扫到这个连通块时就能得到。在每次扫到一个新的连通块时将外面那个颜色答案直接 +1 就可以避免被多减的情况了。
至于为什么只会被多减一次,既然拿一个一维的扫描线去扫这个二维的平面,是不可能在中间把外面的区域分成三份还让它们都在某个右边缘合并消失的,除非这不止一个连通块。
判断连通块则可以提前使用一个并查集做,扫描两次就好了
实现过程不难交给读者
下面的代码经过格式化后十分可读
注释也十分详细

//携带注释的版本
#include<bits/stdc++.h>
#define int long long
using namespace std;
#define lc(x) (x << 1)
#define rc(x) ((x << 1) | 1)
const int N = 2e5 + 5;
int n;
int fa[N];
// 并查集初始化
//并查集维护矩形连通性
inline void setup() {for (int i = 1; i <= n; i++) fa[i] = i;}
// 查找根节点
inline int find(int x) {
    if (x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}
// 合并集合
inline void merge(int x, int y) {fa[find(x)] = find(y);}
// 线段树节点定义
//线段树的节点维护当前区间内存在的所有横边的位置。
struct node {
    int l, r, sum, tag;
} a[N << 4];
// 建树
inline void build(int p, int l, int r) {
    a[p].l = l, a[p].r = r;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(lc(p), l, mid);
    build(rc(p), mid + 1, r);
}
// 下传标记
inline void push_down(int p) {
    if (!a[p].tag) return;
    int tag = a[p].tag;
    a[p].tag = 0;
    if (a[lc(p)].sum) 
        if (!a[lc(p)].tag) a[lc(p)].tag = tag;
        else merge(tag, a[lc(p)].tag);
    if (a[rc(p)].sum) 
        if (!a[rc(p)].tag) a[rc(p)].tag = tag;
        else merge(tag, a[rc(p)].tag);
}
// 更新节点值
inline void push_up(int p) {a[p].sum = a[lc(p)].sum + a[rc(p)].sum;}
// 单点修改
inline void add(int p, int ad, int x) {
    int l = a[p].l, r = a[p].r;
    if (r < ad || l > ad) return;
    if (l == r) {
        a[p].sum += x;
        return;
    }
    push_down(p);
    add(lc(p), ad, x);
    add(rc(p), ad, x);
    push_up(p);
}
// 区间修改
inline void modify(int p, int ml, int mr, int rect) {
    int nl = a[p].l, nr = a[p].r;
    if (nr < ml || nl > mr) return;
    if (nl >= ml && nr <= mr) {
        if (!a[p].sum) return;
        if (!a[p].tag) a[p].tag = rect;
        else merge(rect, a[p].tag);
        return;
    }
    pd(p);
    modify(lc(p), ml, mr, rect);
    modify(rc(p), ml, mr, rect);
}
// 查询区间和
inline int sum(int p, int sr) {
    int nl = a[p].l, nr = a[p].r;
    if (nl > sr) return 0;
    if (nr <= sr) return a[p].sum;
    return sum(lc(p), sr) + sum(rc(p), sr);
}
// 线段定义
struct line {
    int yd, yu, rect;
    bool in;
};
// 垂直线段
line l[N];
bool vis[N];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> n >> t;
    for (int i = 1, xa, xb, ya, yb; i <= n; i++) {
        cin >> xa >> ya >> xb >> yb;
        l[xa] = {ya, yb, i, 1};
        l[xb] = {ya, yb, i, 0};
    }
    setup(),build(1, 1, n << 1);
    //第一次扫描时,每次碰到一个竖边,把它与它覆盖的所有横边对应的矩形合并
    for (int i = 1; i <= (n << 1); i++) {
        int yu_= l[i].yu, yd = l[i].yd;
        modify(1, yd, yu, l[i].rect);
        if (l[i].in)add(1, yd, 1),add(1, yu, 1);
        else add(1, yd, -1),add(1, yu, -1);
    }
    int wcnt = 1, bcnt = 0;
    for (int i = 1; i <= (n << 1); i++) {
        if (l[i].in) {
            //加边
            int d = sum(1, l[i].yd), u_= sum(1, l[i].yu);
            int ch = u_- d + 1; // 改变了几个单位
            bool col = d % 2;   // 最下面的颜色(1 黑 0 白)
            if (!vis[find(l[i].rect)]) {//这是一个新的连通块
                vis[find(l[i].rect)] = 1;
                if(col)bcnt++;
                else wcnt++;
            }
            if (col)wcnt += (ch + 1) / 2,bcnt += ch / 2;
            else bcnt += (ch + 1) / 2,wcnt += ch / 2;
            add(1, l[i].yd, 1),add(1, l[i].yu, 1);
        } else {
            add(1, l[i].yd, -1),add(1, l[i].yu, -1);
            int d = sum(1, l[i].yd), u_= sum(1, l[i].yu),ch = u_- d - 1; // 改变了几个单位
            bool col = d % 2;   // 最下面的颜色(1 黑 0 白)
            if (u_== d) {//特殊情况
                if (col) bcnt--;
                else wcnt--;
            } else {
                if (col)wcnt += (ch + 1) / 2,bcnt += ch / 2;
                else bcnt += (ch + 1) / 2,wcnt += ch / 2;
            }
        }
    }

    if (t == 1) cout << wcnt + bcnt;
    else cout << wcnt << " " << bcnt;
    return 0;
}

T3一部分借鉴洛谷题解区
T1大部分来自于机房大佬cff的题解
我太弱了自己写不出来只好乱借鉴了。

posted @ 2025-08-14 20:54  zhuoheng  阅读(7)  评论(0)    收藏  举报