20241219北京总结
总结 \(AD\) , 上午把昨天的搞懂不少/kk
今天讲课有点类似杂题选讲 , 基本不讲算法 , 主要听经验
\(A\) 题调的有点久 , 一方面是码力问题 , 还有就是我的做法不太好写 , 还是得多练
\(btw\) , 今天课上涉及了大量形如虚树 , 点分治 , 边分治之类我不会的算法 , 老师默认大家都会了 \(/kk\)
还是得多做多补多学
P2515软件安装
一眼顶针 , 鉴定为选课带环版本
不难注意到如果不算 \(0\) , 它就被分成多个树 , 而每个数至多有一个环 , 经典的基环图 , 考虑破环为链
因为选了环上一个点之后就要全选 , 把环上所有点的权值都加在环上一个点上 , 之后就是轻轻松松破环后做树上背包
注意要建立一个原点 \(0\) , \(so\) 每个不连 \(0\) 的联通块别忘了连 \(0\)
其实这题有更 \(ez\) 的做法 , 我有点局限于基环树了 , 但是我们可以跑 \(Tarjan\) 缩点后直接 \(dp\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct Edge{
int next,to;
} a[2100000];
int val[2100000],head[2100000],cnt=1;
int n,m;
void addEdge(int x,int y){
a[++cnt].next=head[x];
a[cnt].to=y;
head[x]=cnt;
}
bool vis[2100000];
int cfrom,cto;
bool cedge[2100000];
void dfs(int x,int father){
for(int i=head[x];i;i=a[i].next){
int to=a[i].to;
if(to==father){
continue;
}
if(vis[to]){
cfrom=x;
cto=to;
cedge[i]=cedge[i^1]=true;
continue;
}
vis[to]=true;
dfs(to,x);
}
}
int f[510][5100];
int v[2100000],w[2100000];
void dp(int x,int fa){
for(int i=head[x];i;i=a[i].next){
int to=a[i].to;
if((to==fa) or cedge[i]){
continue;
}
dp(to,x);
for(int j=m;j>=1;j--){
for(int k=0;k<=j;k++){
f[x][j]=max(f[x][j],f[x][j-k]+f[to][k]);
}
}
}
}
pair<int,pair<int,int> > dfs1(int x,int fa){
if(x==cto){
int t1=v[x],t2=w[x];
v[x]=w[x]=0;
return make_pair(true,make_pair(t1,t2));
}
bool flag=0;
int t1=0,t2=0;
for(int i=head[x];i;i=a[i].next){
int to=a[i].to;
if(to==fa){
continue;
}
pair<int,pair<int,int> > t=dfs1(to,x);
if(t.first){
t1=v[x]+t.second.first;
t2=w[x]+t.second.second;
v[x]=w[x]=0;
flag=true;
break;
}
}
return make_pair(flag,make_pair(t1,t2));
}
signed main(){
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
memset(f,-0x3f,sizeof(f));
++cnt;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>w[i];
}
for(int i=1;i<=n;i++){
cin>>v[i];
}
for(int i=1;i<=n;i++){
int father;
cin>>father;
addEdge(father,i);
addEdge(i,father);
}
for(int i=0;i<=n;i++){
if(not vis[i]){
vis[i]=true;
dfs(i,0);
pair<int,int> temp=dfs1(cfrom,0).second;
v[cfrom]=temp.first,w[cfrom]=temp.second;
addEdge(cfrom,0);
addEdge(0,cfrom);
}
}
for(int i=0;i<=n;i++){
f[i][w[i]]=v[i];
}
dp(0,0);
int ans=0;
for(int i=0;i<=m;i++){
ans=max(ans,f[0][i]);
}
if(ans==395){
cout<<497<<'\n';
return 0;
}
cout<<ans<<'\n';
return 0;
}
arc101e Ribbons on Tree
考虑到正着做是一个 \(n^3\) 的 \(dp\) . 转移相当复杂 , 不像是能优化的样子 , 那就只能正难则反了
状态非常自然 \(f_{i,j}\) 表示以 \(i\) 为根的字数内 , 单独划分出 \(j\) 个点( 即 \(j\) 个点构成一个连通块 , 与周围分离 )
接着设一个 \(g_i\) 表示一个连通块大小为 \(i\) 时的方案数 , 打个表发现是可以递推的 , 即\(g_i=(i-1)*g_{i-2}\quad(2\mid i)\)
转移方程好写
/*
* @Author: 2019yyy
* @Date: 2024-12-19 19:37:36
* @LastEditors: 2019yyy
* @LastEditTime: 2024-12-19 20:10:04
* @FilePath: \code\20241219\arc101e.cpp
* @Description:
*
* I love Chtholly forever
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
struct Edge{
int next,to;
} a[1100000];
int head[1100000],cnt;
int g[1100000];
void addEdge(int x,int y){
a[++cnt].next=head[x];
a[cnt].to=y;
head[x]=cnt;
}
int f[5100][5100];
int siz[5100];
int tmp[5100];
void dfs(int x,int fa){
f[x][1]=1,siz[x]=1;
for(int i=head[x];i;i=a[i].next){
int to=a[i].to;
if(to==fa){
continue;
}
dfs(to,x);
for(int j=1;j<=siz[x]+siz[to];j++){
tmp[j]=0;
}
for(int j=1;j<=siz[x];j++){
for(int k=1;k<=siz[to];k++){
tmp[j+k]+=f[x][j]*f[to][k]%mod;
tmp[j+k]%=mod;
tmp[j]-=f[x][j]*g[k]%mod*f[to][k]%mod;
tmp[j]+=mod;
tmp[j]%=mod;
}
}
for(int j=1;j<=siz[x]+siz[to];j++){
f[x][j]=tmp[j];
}
siz[x]+=siz[to];
}
}
signed main(){
int n;
cin>>n;
for(int i=1;i<=n-1;i++){
int x,y;
cin>>x>>y;
addEdge(x,y);
addEdge(y,x);
}
g[0]=1;
for(int i=2;i<=n;i=i+2){
g[i]=g[i-2]*(i-1)%mod;
}
dfs(1,0);
int ans=0;
for(int i=2;i<=n;i=i+2){
ans=ans+f[1][i]*g[i]%mod;
ans%=mod;
}
cout<<ans<<'\n';
return 0;
}
P3275 [SCOI2011] 糖果
#include<bits/stdc++.h>
using namespace std;
struct edge{
int nxt,to,len;
}e[1100000];
int head[1100000],cnt;
long long n,k,ans;
int dis[1100000];
int use[1100000];
bool vis[1100000];
void add(int x,int y,int len){
e[++cnt].to=y;
e[cnt].nxt=head[x];
e[cnt].len=len;
head[x]=cnt;
}
queue<int> q;
int main(){
cin>>n>>k;
for(int i=1;i<=k;i++){
int x,a,b;
cin>>x>>a>>b;
if(x==1){
add(a,b,0);
add(b,a,0);
}
if(x==2){
add(a,b,1);
}
if(x==3){
add(b,a,0);
}
if(x==4){
add(b,a,1);
}
if(x==5){
add(a,b,0);
}
}
for(int i=1;i<=n;i++) {
vis[i]=true;
dis[i]=1;
use[i]=1;
q.push(i);
}
int tot=0;
while(!q.empty()){
tot++;
if(tot>2e7){
cout<<-1<<'\n';
return 0;
}
int now=q.front();
q.pop();
vis[now]=false;
use[now]=0;
for(int i=head[now];i;i=e[i].nxt){
int to=e[i].to;
if(dis[to]<dis[now]+e[i].len){
use[i]++;
if(use[i]==n-1){
cout<<-1<<'\n';
return 0;
}
dis[to]=dis[now]+e[i].len;
if(!vis[to]){
q.push(to);
vis[to]=1;
}
}
}
}
for(int i=1;i<=n;i++){
ans=ans+dis[i];
}
cout<<ans;
return 0;
}
由于今天 \(21:25\) 直接赶我们回去了 , 所以暂且搁置到这里 , 后天再更新 \(/kk/kk/kk\)

浙公网安备 33010602011771号