【考试总结】2022-02-12
好
区间
设 表示将 全部删空的能得到的最大价值, 表示可以 不删完所能得到的最大价值
转移 首先考虑 不在同一次操作中被删除的情况,那么可以枚举分界线
而对于在同一次操作中被删除的情况考虑该次操作删除的数字的值域形态:单峰
那么尝试计算 分别表示保留 和保留 的情况下将序列删成加一递增的最大获利和删成减一递减的最大获利即可
这两个状态的转移根据定义,枚举那些和 差一的位置,该删空的删掉就行了
Code Display
const int N=410,inf=0x3f3f3f3f3f3f3f3f;
int a[N],v[N],ans[N],n,f[N][N],incr[N][N],decr[N][N];
signed main(){
freopen("good.in","r",stdin); freopen("good.out","w",stdout);
n=read();
rep(i,1,n) v[i]=read();
rep(i,1,n) rep(j,1,i-1) ckmax(v[i],v[j]+v[i-j]);
rep(i,1,n) a[i]=read();
memset(incr,-0x3f,sizeof(incr));
memset(decr,-0x3f,sizeof(decr));
memset(f,-0x3f,sizeof(f));
rep(i,1,n){
f[i][i]=v[1];
f[i+1][i]=incr[i][i]=decr[i][i]=0;
}
f[1][0]=0;
for(int len=2;len<=n;++len){
for(int l=1;l+len-1<=n;++l){
int r=l+len-1;
rep(i,l,r-1) ckmax(f[l][r],f[l][i]+f[i+1][r]);
rep(i,l+1,r) if(a[i]==a[l]-1){
ckmax(decr[l][r],decr[i][r]+f[l+1][i-1]);
}
rep(i,l,r-1) if(a[i]==a[r]-1){
ckmax(incr[l][r],incr[l][i]+f[i+1][r-1]);
}
rep(i,l,r){
if(incr[l][i]==inf||decr[i][r]==inf) continue;
if(a[i]<a[l]||a[i]<a[r]) continue;
ckmax(f[l][r],v[a[i]-a[l]+a[i]-a[r]+1]+incr[l][i]+decr[i][r]);
}
}
}
for(int i=1;i<=n;++i){
ans[i]=ans[i-1];
for(int j=i;j>=1;--j) ckmax(ans[i],ans[j-1]+f[j][i]);
}
print(ans[n]);
return 0;
}
色
不难发现这个最短距离是一条边的长度,这条边一定在全图的 上
关注到答案的来源一定是父亲和儿子的边,那么对于每个父亲,维护每种颜色的儿子在 上返祖边的权值,并将与自己异色的每个颜色里面最小值放到全局维护的 std::multiset
上
在修改颜色的时候更改当前点 的父亲处维护的信息,然后再考虑与其儿子的连边中产生对 std::multiset
的变化即可
时间复杂度
Code Display
const int N=3e5+10;
multiset<int> ans;
int n,m,col[N],val[N],Q,fa[N];
struct edge{int u,v,w;}e[N];
struct DSU{
int fa[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){return fa[find(x)]=find(y),void();}
inline void init(){rep(i,1,n) fa[i]=i; return ;}
}Dsu;
vector<pii> g[N];
map<int,multiset<int> >mp[N];
inline void get_fa(int x,int fat){
fa[x]=fat;
for(auto e:g[x]) if(e.fir!=fat) val[e.fir]=e.sec,get_fa(e.fir,x);
return ;
}
inline void del(int x,int mycol){
if(!fa[x]) return ;
assert(mp[fa[x]].count(mycol));
multiset<int> &now=mp[fa[x]][mycol];
assert(now.find(val[x])!=now.end());
now.erase(now.find(val[x]));
if(!now.size()||(*now.begin())>val[x]){
if(mycol!=col[fa[x]]){
assert(ans.find(val[x])!=ans.end());
ans.erase(ans.find(val[x]));
}
if(!now.size()){
mp[fa[x]].erase(mycol);
}else{
if(mycol!=col[fa[x]]) ans.insert(*now.begin());
}
}
return ;
}
inline void ins(int x,int mycol){
if(!fa[x]) return ;
if(!mp[fa[x]].count(mycol)){
mp[fa[x]][mycol].insert(val[x]);
if(mycol!=col[fa[x]]) ans.insert(val[x]);
return ;
}
assert(mp[fa[x]][mycol].size());
if(mycol!=col[fa[x]]){
assert(ans.find(*mp[fa[x]][mycol].begin())!=ans.end());
ans.erase(ans.find(*mp[fa[x]][mycol].begin()));
}
mp[fa[x]][mycol].insert(val[x]);
if(mycol!=col[fa[x]]) ans.insert(*mp[fa[x]][mycol].begin());
return ;
}
signed main(){
freopen("color.in","r",stdin); freopen("color.out","w",stdout);
n=read(); m=read(); read(); Q=read();
rep(i,1,m){
int u=read(),v=read(),w=read();
e[i]={u,v,w};
}
sort(e+1,e+m+1,[&](const edge a,const edge b){return a.w<b.w;});
Dsu.init();
for(int i=1;i<=m;++i){
int u=e[i].u,v=e[i].v;
if(Dsu.find(u)==Dsu.find(v)) continue;
Dsu.merge(Dsu.find(u),Dsu.find(v));
g[u].emplace_back(v,e[i].w);
g[v].emplace_back(u,e[i].w);
}
get_fa(1,0);
rep(i,1,n) col[i]=read();
rep(i,1,n) ins(i,col[i]);
while(Q--){
int x=read(),c=read();
if(c==col[x]){
print(*ans.begin());
continue;
}
del(x,col[x]);
if(mp[x].count(col[x])) ans.insert(*mp[x][col[x]].begin());
if(mp[x].count(c)) ans.erase(ans.find(*mp[x][c].begin()));
ins(x,col[x]=c);
col[x]=c;
print(*ans.begin());
}
return 0;
}
乐
注意 ,那么设 表示已考虑 的前缀,其最小 是 的方案数
转移考虑删掉最小 是 的方案数即可,形式化地讲:
使用前缀积维护,那么显然该和 有关就只和 有关,还有一项之和 有关,使用半在线卷积求就行了
问题的限制是 的时候才能转移,那么在半在线卷积的过程中,分成 的部分直接乘和 的部分再写一个分治即可
注意这个额外的分治只会在 时调用,那么复杂度仍然是 ,配合 全相同时 求解即可通过了!
Code Display
int cl,cr;
int f[N],g[N],tmpf[N],tmpg[N];
inline void solve2(int l1,int r1,int l2,int r2){
if(2*r2<cl) return ;
if(r2<l1||r1<l1||r2<l2) return ;
if(l1==r1&&l2==r2){
if(l1+l2>=cl&&l2+l1<=cr) ckadd(f[l1+l2],mul(prd[l2],mul(iprd[l1],f[l1])));
return ;
}
int mid=(l1+r1)>>1;
if(r2<=mid) return solve2(l1,mid,l2,r2);
int lim=1; while(lim<=(r2-l1+1)<<1) lim<<=1;
rep(i,0,lim-1) tmpf[i]=tmpg[i]=0;
for(int i=mid+1;i<=r2;++i) tmpg[i-mid-1]=prd[i];
for(int i=l1;i<=mid;++i) tmpf[i-l1]=mul(iprd[i],f[i]);
NTT(tmpf,lim,1); NTT(tmpg,lim,1);
for(int i=0;i<lim;++i) ckmul(tmpf[i],tmpg[i]);
NTT(tmpf,lim,-1);
for(int i=max(cl,l1+mid+1);i<=min(cr,r2+mid);++i) ckadd(f[i],tmpf[i-mid-l1-1]);
solve2(l1,mid,l2,mid); solve2(mid+1,r1,mid+1,r2);
return ;
}
int lst;
inline void solve(int l,int r){
if(l==r){
f[l]=del(prd[l],f[l]);
return ;
}
int mid=(l+r)>>1; solve(l,mid);
if(r-l>=mid+1){
int lim=1; while(lim<=((r-l+1)<<1)) lim<<=1;
rep(i,0,lim-1) tmpf[i]=tmpg[i]=0;
for(int i=mid+1;i<=r-l;++i) tmpg[i-mid-1]=prd[i];
for(int i=l;i<=mid;++i) tmpf[i-l]=mul(iprd[i],f[i]);
NTT(tmpf,lim,1); NTT(tmpg,lim,1);
for(int i=0;i<lim;++i) ckmul(tmpf[i],tmpg[i]);
NTT(tmpf,lim,-1);
for(int i=mid+1+l;i<=r;++i) ckadd(f[i],tmpf[i-l-mid-1]);
}
if(r-l>=l){
cl=mid+1; cr=r;
solve2(lst+1,mid,lst+1,mid);
solve2(1,lst,lst+1,mid);
lst=mid;
}
return solve(mid+1,r);
}
bool sam;
int dp[N];
signed main(){
freopen("music.in","r",stdin); freopen("music.out","w",stdout);
n=read(); prd[0]=iprd[0]=1;
sam=1;
rep(i,1,n){
v[i]=read();
sam&=v[1]==v[i];
prd[i]=mul(prd[i-1],v[i]),iprd[i]=ksm(prd[i],mod-2);
}
if(sam){
int pw=1,sum=0;
rep(i,1,n){
ckmul(pw,v[1]);
dp[i]=mul(pw,del(1,sum));
if(i&1){
int tar=i/2+1;
ckadd(sum,mul(dp[tar],ksm(v[1],mod-1-2*tar)));
}
}
print(dp[n]);
exit(0);
}
solve(1,(n+1)/2);
for(int i=1;2*i<=n;++i) ckadd(f[n],mul(prd[n-i],mul(iprd[i],f[i])));
print(del(prd[n],f[n]));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律