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\)

posted @ 2024-12-19 21:27  2019yyy  阅读(12)  评论(0)    收藏  举报