T1 White and Black 0Pts
原題 Lights Out on Tree。
顯然不存在無解情況,因為可以從根開始 dfs,遇到黑點就翻轉。
所以每個點最多隻會翻轉一次,且與順序無關,所以翻轉方式只有一種。
有多測,不能暴力模擬。
手玩一下可以發現如果 \(col_u\) 與 \(col_{fa_u}\) 不同就會貢獻一次翻轉;
簡單處理即可。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,q,fa[N];
int x,a[N],col[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>q;
for(int i=2;i<=n;i++){
cin>>fa[i];
col[fa[i]]++;
}
while(q--){
cin>>x;
int ans=x;
for(int i=1;i<=x;i++){
cin>>a[i];
col[fa[a[i]]]-=2;
}
for(int i=1;i<=x;i++){
ans+=col[a[i]];
}
for(int i=1;i<=x;i++){
col[fa[a[i]]]+=2;
}
cout<<ans<<"\n";
}
return 0;
}
T2 White and White 0Pts
原題 Encryption (hard)
首先考慮暴力。複雜度 \(O(n^2k)\),對應 Easy Vision;
然後發現 \(k\) 很小,可以透過列舉模數的位置對應的 dp 最小值,可以做到 \(O(nkp)\),對應 Medium Vision;
考慮最佳化最初的暴力 \(O(n^2k)\),發現 \(f_{i,j} \equiv \pmod p\),於是可以直接記錄 \(f_{*,j}\) 的最小值進行轉移,複雜度 \(O(nk)\)。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n,k,p,f[N][101];
int g[2][101];
int sum[N];
int main(){
cin>>n>>k>>p;
for(int i=1;i<=n;i++){
cin>>sum[i];
sum[i]=(sum[i]+sum[i-1])%p;
}
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=k;j++){
int lst=g[i-1&1][j-1];
f[i][j]=min(f[i][j],f[lst][j-1]+(sum[i]-sum[lst]+p)%p);
}
for(int j=0;j<=k;j++){
g[i&1][j]=g[i-1&1][j];
if(f[i][j]<f[g[i&1][j]][j])g[i&1][j]=i;
}
}
cout<<f[n][k];
return 0;
}
T3 Black and Black 10Pts
原題 ± Increasing Sequence。
首先構造一個 \(1\) ~ \(n\) 的排列,然後算出此時的 \(s\);
如果此時 \(s=0\) 直接輸出就行了;
如果 \(s>0\),考慮尋找一個為正的字首或者一個為負的字尾,並將其加上或減去 \(s\) 即可;反之則一定無解。
\(s<0\) 的情況同理。
點選檢視程式碼
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,a[N],s,sum[N],mus[N];
void change(int st,int x,int op){
cout<<"Yes\n";
if(op){
for(int i=1;i<=n;i++){
if(i>=st)cout<<i+x<<" ";
else cout<<i<<" ";
}
}
else{
for(int i=1;i<=n;i++){
if(i<=st)cout<<i-x<<" ";
else cout<<i<<" ";
}
}
}
main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
s+=a[i]*i;
}
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+a[i];
}
for(int i=n;i>=1;i--){
mus[i]=mus[i+1]+a[i];
}
if(s==0)return change(0,0,1),0;
if(s>0){
int p=0,f=0;
for(int i=1;i<=n;i++){
if(sum[i]==1)p=i;
if(mus[i]==-1)f=i;
}
if(!p&&!f)return cout<<"No\n",0;
if(p)change(p,s,0);
else change(f,s,1);
}
else{
int p=0,f=0;
for(int i=1;i<=n;i++){
if(sum[i]==-1)p=i;
if(mus[i]==1)f=i;
}
if(!p&&!f)return cout<<"No\n",0;
if(p)change(p,-s,0);
else change(f,-s,1);
}
return 0;
}
T4 Black and White 60Pts
原題 捉迷藏。
括號序列,因為好賀
大體意思是在 DFS 序中加入括號來表示樹的結構,此時兩個點間消去匹配括號的括號數量即為兩點間距離;
然後就又是一道山海經。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5;
struct edge{
int next,to;
}e[N];
int h[N],cnt;
void add(int u,int v){
e[++cnt]={h[u],v};
h[u]=cnt;
}
int n,q,x;
char ch;
int s[N*3],tot,num;
int in[N];
bool c[N];
void dfs(int x,int fa){
s[++tot]=-1;
s[++tot]=x;
in[x]=tot;
for(int i=h[x];i;i=e[i].next){
int to=e[i].to;
if(to==fa)continue;
dfs(to,x);
}
s[++tot]=-2;
}
struct tree{
int a,b;
int l1,l2;
int r1,r2;
int dis;
}t[N<<2];
void init(int k,int x){
t[k].a=t[k].b=0;
t[k].l1=t[k].l2=t[k].r1=t[k].r2=t[k].dis=-1e9;
if(s[x]==-1)t[k].b=1;
else if(s[x]==-2)t[k].a=1;
else if(!c[s[x]])t[k].l1=t[k].r1=t[k].l2=t[k].r2=0;
}
void pushup(int k){
if(t[k<<1].b>t[k<<1|1].a){
t[k].a=t[k<<1].a;
t[k].b=t[k<<1].b-t[k<<1|1].a+t[k<<1|1].b;
}
else{
t[k].b=t[k<<1|1].b;
t[k].a=t[k<<1|1].a-t[k<<1].b+t[k<<1].a;
}
t[k].l1=max({t[k<<1].l1,t[k<<1|1].l1+t[k<<1].a-t[k<<1].b,t[k<<1|1].l2+t[k<<1].a+t[k<<1].b});
t[k].l2=max(t[k<<1].l2,t[k<<1|1].l2-t[k<<1].a+t[k<<1].b);
t[k].r1=max({t[k<<1|1].r1,t[k<<1].r1+t[k<<1|1].b-t[k<<1|1].a,t[k<<1].r2+t[k<<1|1].a+t[k<<1|1].b});
t[k].r2=max(t[k<<1|1].r2,t[k<<1].r2+t[k<<1|1].a-t[k<<1|1].b);
t[k].dis=max({t[k<<1].r1+t[k<<1|1].l2,t[k<<1].r2+t[k<<1|1].l1,t[k<<1].dis,t[k<<1|1].dis});
}
void build(int k,int l,int r){
if(l==r){
init(k,l);
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void update(int k,int l,int r,int pos){
if(l==r){
init(k,l);
return;
}
int mid=(l+r)>>1;
if(pos<=mid)update(k<<1,l,mid,pos);
else update(k<<1|1,mid+1,r,pos);
pushup(k);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1,x,y;i<n;i++){
cin>>x>>y;
add(x,y);
add(y,x);
}
dfs(1,0);
num=n;
build(1,1,tot);
cin>>q;
while(q--){
cin>>ch;
if(ch=='C'){
cin>>x;
num+=c[x]?1:-1;
c[x]^=1;
update(1,1,tot,in[x]);
}
else{
if(num==0)cout<<"-1\n";
else if(num==1)cout<<"0\n";
else cout<<t[1].dis<<"\n";
}
}
return 0;
}