比赛链接。
细节题。一年前竟然 AC 乐。
考虑 0 0 0 的数量,设其为 c n t cnt cnt:
int a[4], b[4], cnta, cntb;
void solve(){
for(int i = 0; i < 4; ++ i){
a[i] = rdi;
}
for(int i = 0; i < 4; ++ i){
b[i] = rdi;
}
for(int i = 0; i < 4; ++ i){
if(a[i]){
++ cnta;
}
if(b[i]){
++ cntb;
}
}
if(cnta != cntb){
puts("No");
return;
} else if(cnta <= 2){
puts("Yes");
return;
} else if(cnta == 4){
if(a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3]){
puts("Yes");
return;
} else {
puts("No");
return;
}
} else {
if(a[0] == 0){
a[0] = a[1];
a[1] = a[3];
a[3] = a[2];
} else if(a[1] == 0){
a[1] = a[3];
a[3] = a[2];
} else if(a[3] == 0){
a[3] = a[2];
}
if(b[0] == 0){
b[0] = b[1];
b[1] = b[3];
b[3] = b[2];
} else if(b[1] == 0){
b[1] = b[3];
b[3] = b[2];
} else if(b[2] == 0){
b[3] = b[2];
}
for(int i = 0; i < 3; ++ i){
int tmp = a[0];
a[0] = a[1];
a[1] = a[2];
a[2] = tmp;
if(a[0] == b[0] && a[1] == b[1] && a[2] == b[2]){
puts("Yes");
return;
}
}
}
puts("No");
}
一年前打比赛的时候没做出来。
因为 + 3,+ 4 和 + 7 没什么不同,所以我们考虑对于所有操作分为四类:加正数、加负数、乘正数、乘负数,并相对求出各自的和、积,分别记为
s
a
a
,
s
a
b
,
p
m
a
,
p
m
b
saa,sab,pma,pmb
saa,sab,pma,pmb。
因为乘数的绝对值 ≥ 2 \ge 2 ≥2,所以乘法一定比加法更优。
如果并没有“乘负数”的操作,答案即为 s a a ∗ p m a + s a b saa*pma+sab saa∗pma+sab。
否则设“乘负数”操作中乘数的最大值为 t m p tmp tmp(因为都是负的,所以这个数的绝对值最小):
const ll P = 998244353;
vector<int> ma, mb, aa, ab;
int n;
void solve(){
n = rdi;
ll pma = 1, tmp = 1, pmb = 1, saa = 0, sab = 0;
for(int i = 1; i <= n; ++ i){
char ch;
cin >> ch;
int a = rdi;
if(ch == '+'){
if(a > 0){
aa.push_back(a);
saa += a;
} else {
a = -a;
ab.push_back(a);
sab += a;
}
} else {
if(a > 0){
ma.push_back(a);
pma = pma * a % P;
} else {
a = -a;
mb.push_back(a);
}
}
}
if(mb.size() == 0){
writen((saa % P * pma % P - sab % P + P) % P);
return;
}
sort(mb.begin(), mb.end());
tmp = mb[0];
for(int i = 1; i < mb.size(); ++ i){
pmb = pmb * mb[i] % P;
}
if(mb.size() & 1){
writen((sab % P * tmp % P + saa % P) % P * pma % P * pmb % P);
} else {
writen((saa % P * tmp % P + sab % P) % P * pma % P * pmb % P);
}
}
有点困难。
首先考虑 1 1 1 的可能位置。可以查询第 ⌈ n 2 ⌉ \lceil\dfrac n2\rceil ⌈2n⌉ 层的 1 1 1 的个数,若有 k k k 个 1 1 1, 1 1 1 的位置即为 k k k 或 n − k + 1 n-k+1 n−k+1。这里我们任取其一即可,后面会解释为什么。
然后考虑 2 2 2。 1 1 1 的位置把原区间 [ 1 , n ] [1,n] [1,n] 分成了两个子区间 [ 1 , p o s 1 − 1 ] , [ p o s 1 + 1 , n ] [1,pos_1-1],[pos_1+1,n] [1,pos1−1],[pos1+1,n]。对于区间 [ l , r ] [l,r] [l,r],我们查询第 r − l + 1 , r − l + 2 r-l+1,r-l+2 r−l+1,r−l+2 层,若前者有 2 2 2,后者没有 2 2 2,则 2 2 2 可以处于区间 [ l , r ] [l,r] [l,r] 中。那么把 [ l , r ] [l,r] [l,r] 替换为 [ 1 , p o s 1 − 1 ] , [ p o s 1 + 1 , n ] [1,pos_1-1],[pos_1+1,n] [1,pos1−1],[pos1+1,n],查询即可。若两个区间均可,也是任取其一都行。然后按照查询 1 1 1 的位置一样查询 2 2 2 的位置。
其它数同理,只不过是要查询更多的区间。这里可以使用链表维护每个数的位置、它后面的数。
为什么对于多个可行的答案任选其一都可以呢?因为整个解法我们只考虑了各个区间的长度,对于多个可行的答案,其实他们分成的每个区间的长度所形成的集合是相同的,所以任选其一都可以。
const int N = 1010;
int n, cnt[N][N];
struct Link{
int pos, nxt;
} lnk[N];
void solve(){
n = rdi;
for(int i = 1; i <= n; ++ i){
for(int j = i; j <= n; ++ j){
++ cnt[i][rdi];
}
}
lnk[0].pos = 0;
lnk[n+1].pos = n+1;
lnk[0].nxt = n+1;
for(int i = 1; i <= n; ++ i){
for(int j = 0; j <= n; j = lnk[j].nxt){
int len = lnk[lnk[j].nxt].pos - lnk[j].pos - 1;
if(cnt[len][i] && !cnt[len+1][i]){
lnk[i].pos = lnk[j].pos + cnt[len+1>>1][i];
lnk[i].nxt = lnk[j].nxt;
lnk[j].nxt = i;
break;
}
}
}
for(int i = lnk[0].nxt; i <= n; i = lnk[i].nxt){
write(i);
}
}
考虑树形 DP。设四个状态:
则可以列出转移方程:
f x , 0 = ∏ y ∈ S o n x ( f y , 0 [ a x = a y ] , f y , 2 [ a x ≠ a y ] ) f_{x,0}=\prod_{y\in Son_x}(f_{y,0}[a_x=a_y],f_{y,2}[a_x\neq a_y]) fx,0=y∈Sonx∏(fy,0[ax=ay],fy,2[ax=ay])
f x , 1 = ∏ y ∈ S o n x ( f y , 0 + f y , 1 + f y , 2 ) − f x , 0 f_{x,1}=\prod_{y\in Son_x}(f_{y,0}+f_{y,1}+f_{y,2})-f_{x,0} fx,1=y∈Sonx∏(fy,0+fy,1+fy,2)−fx,0
f x , 2 = ∏ y ∈ S o n x ( f y , 0 [ a x = a y ] + f y , 1 + f y , 2 + f y , 3 ) f_{x,2}=\prod_{y\in Son_x}(f_{y,0}[a_x=a_y]+f_{y,1}+f_{y,2}+f_{y,3}) fx,2=y∈Sonx∏(fy,0[ax=ay]+fy,1+fy,2+fy,3)
f x , 3 = [ a f a = a x ] ∏ y ∈ S o n x ( f y , 2 + f y , 3 ) [ a x = a y ] f_{x,3}=[a_{fa}=a_x]\prod_{y\in Son_x}(f_{y,2}+f_{y,3})[a_x=a_y] fx,3=[afa=ax]y∈Sonx∏(fy,2+fy,3)[ax=ay]
const ll P = 998244353;
const int N = 2e5 + 10;
vector<int> G[N];
int n, a[N];
ll f[N][4];
void dfs(int x, int fa){
f[x][0] = f[x][1] = f[x][2] = 1;
f[x][3] = (a[fa] == a[x] ? 1 : 0);
for(int i = 0; i < G[x].size(); ++ i){
int y = G[x][i];
if(y == fa){
continue;
}
dfs(y, x);
f[x][0] = f[x][0] * (a[x] == a[y] ? f[y][0] : f[y][2]) % P;
f[x][1] = f[x][1] * (f[y][0] + f[y][1] + f[y][2]) % P;
f[x][2] = f[x][2] * ((a[x] == a[y] ? f[y][0] : 0) + f[y][1] + f[y][2] + f[y][3]) % P;
f[x][3] = f[x][3] * (a[x] == a[y] ? f[y][2] + f[y][3] : 0) % P;
}
f[x][1] = (f[x][1] - f[x][0] + P) % P;
}
void solve(){
n = rdi;
for(int i = 1; i <= n; ++ i){
a[i] = rdi;
}
for(int i = 1; i < n; ++ i){
int x = rdi;
int y = rdi;
G[x].push_back(y);
G[y].push_back(x);
}
a[0] = a[1];
dfs(1, 0);
printf("%lld\n", (f[1][1] + f[1][2] + f[1][3]) % P);
return;
}