其他分享
首页 > 其他分享> > 9.15-CSP-S开小灶4

9.15-CSP-S开小灶4

作者:互联网

T1 山洞

比较简单但是我脑子抽了绕了半天弯。
朴素dp是很好写的
$ dp[i][j]= dp[i-1][j-i]+dp[i-1][j+i] ,(j-i \neq j+i)$

考虑优化,我们可以先暴力推出前n步(当然最大可以推到10000步好像也没问题),然后我们一次走n步,这样会一共走m/n次,然后剩下m%n次仍然暴力推就行。中间一次走n步的实际上就是循环卷积,可以倍增卷,于是就切了。

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=1e3+10,Mod=1e9+7,inv2=500000004;
int pw(int x,int p){int res=1,base=x;while(p){if(p&1)res=1LL*res*base%Mod;base=1LL*base*base%Mod;p>>=1;}return res;}
int Inv(int x){return pw(x,Mod-2);}
int dp[maxn][maxn];
int n,m;
int bs[maxn];
int Ans[maxn];

void Work1(){
	dp[0][0]=1;
	for(int i=1;i<=m;++i){
		for(int j=0;j<n;++j){
			int pre=(j+n-(i%n))%n,nxt=(j+i)%n;
			dp[i][nxt]=(dp[i][nxt]+dp[i-1][j])%Mod;
			if(pre!=nxt)dp[i][pre]=(dp[i][pre]+dp[i-1][j])%Mod;
		}
	}
	cout<<dp[m][0];
}

int tr[maxn],ta[maxn],tb[maxn];

void Mul(int *res,int *a,int *b){
	for(int i=0;i<n;++i)ta[i]=a[i],tb[i]=b[i];
	for(int i=0;i<n;++i)res[i]=0;
	for(int i=0;i<n;++i)for(int j=0;j<n;++j)
		res[(i+j)%n]=(res[(i+j)%n]+1LL*ta[i]*tb[j])%Mod;
}

void solve(){
	cin>>n>>m; if(m<=n)return Work1();
	dp[0][0]=1;
	for(int i=1;i<=n;++i){
		for(int j=0;j<n;++j){
			int pre=(j+n-(i%n))%n,nxt=(j+i)%n;
			dp[i][nxt]=(dp[i][nxt]+dp[i-1][j])%Mod;
			if(pre!=nxt)dp[i][pre]=(dp[i][pre]+dp[i-1][j])%Mod;
		}
	}
	for(int j=0;j<n;++j)bs[j]=dp[n][j];
	m-=n;int k=m/n;
	for(int j=0;j<n;++j)Ans[j]=bs[j];
	while(k){ if(k&1)Mul(Ans,Ans,bs); Mul(bs,bs,bs); k>>=1; }
	m%=n; memset(dp,0,sizeof(dp));
	for(int j=0;j<n;++j)dp[0][j]=Ans[j];
	for(int i=1;i<=m;++i){
		for(int j=0;j<n;++j){
			int pre=(j+n-(i%n))%n,nxt=(j+i)%n;
			dp[i][nxt]=(dp[i][nxt]+dp[i-1][j])%Mod;
			if(pre!=nxt)dp[i][pre]=(dp[i][pre]+dp[i-1][j])%Mod;
		}
	}
	cout<<dp[m][0];
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }

T2 beauty

挺水的,考虑每条边的贡献,它两边的关键点的量分别\(x\)和\(2 * k - x\),显然能把两边连起来我一定不会在其中一边自己连,于是就是一条边的贡献就是两边点的数量取min,对于方案的构造可以考虑从重心一点一点配。

赛时写了一个需要特判链的做法,拿了非特殊性质的分,本质上与上面的做法相同,就是考虑在一棵子树上的根处给路径配对,对于这个根来讲,显然我能在它的儿子子树间配对就一定不会在儿子子树内配对,像摩尔投票一样,把所有儿子子树的关键点的数量统计出来,只要没有出现绝对众数,那么只在儿子子树间配对的构造就是可行的,否则我们找出关键点最多的儿子子树,子树内其他关键点都和最大子树的关键点配对,然后剩下一部分递归进最大子树内处理,同时记录一下子树内有多少个节点被拿到外边和其他子树的点匹配了,每次递归进一棵子树时,先把对于出去的那些点,子树根和其父亲的连边的贡献算上。有70分,但是在链上的话,显然就是首尾配对,但是这个做法会相邻的配对,于是就锅了。

upd:赛时做法不用特判链,我想麻烦了。显然链上的做法就是每次都拿首尾配对,于是在单儿子下传pre的那部分里,考虑一下儿子子树内的点还够不够拿出去配对,够就给pre加一,否则就把自己拿出去给上边配对,也就是给pre-1,就可以过掉链的情况。

赛时做法


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=1e5+10,maxm=2e5+10;

ll ans;

struct Graph{
	struct eg{int from,to,next;}e[maxm];
	int len,head[maxn];int fa[maxn],val[maxn],siz[maxn];
	int pre[maxn],dis[maxn];
	vector<int>son[maxn];
	void lqx(int from,int to)
	{ e[++len].from=from,e[len].to=to,e[len].next=head[from],head[from]=len; }
	void Dfs(int u){
		siz[u]=val[u];
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to;if(v==fa[u])continue;
			fa[v]=u;son[u].push_back(v);Dfs(v);
			siz[u]+=siz[v];
			dis[u]+=dis[v]+siz[v];
		}
	}
	void Dp(int u){
		int sum=0,mx=0;ans+=pre[u];
		if(son[u].empty())return;
		if(son[u].size()==1){
			if(val[u]){
				if(pre[u]+1>siz[son[u][0]])pre[son[u][0]]+=pre[u]-1;
				else pre[son[u][0]]+=pre[u]+1;
			}else pre[son[u][0]]+=pre[u];
			return Dp(son[u][0]);
		}
		for(auto v : son[u])sum+=siz[v],mx=max(siz[v],mx);
		if(mx-pre[u]>siz[u]-mx){
			int tx=0,tv=0;
			for(auto v : son[u])if(siz[v]!=mx)ans+=dis[v]+siz[v],tx+=siz[v];
			else tv=v;
			pre[tv]+=pre[u]+tx+val[u];
			Dp(tv);
			
		}else{ans+=dis[u];return;}
	}
}G;

int spe[maxn];
int n,k,tpy;

void solve(){
	cin>>n>>k>>tpy;int x,y;
	Rep(i,1,2*k)cin>>spe[i],G.val[spe[i]]=1;
	Rep(i,2,n)cin>>x>>y,G.lqx(x,y),G.lqx(y,x);
	G.Dfs(1);G.Dp(1);
	cout<<ans<<"\n";
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }


题解做法


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=1e5+10,maxm=2e5+10;

ll ans;
int n,k,tpy;

struct Graph{
	struct eg{int from,to,next;}e[maxm];
	int len,head[maxn];int fa[maxn],val[maxn],siz[maxn];
	int pre[maxn],dis[maxn];
	vector<int>son[maxn];
	void lqx(int from,int to)
	{ e[++len].from=from,e[len].to=to,e[len].next=head[from],head[from]=len; }
	void Dfs(int u){
		siz[u]=val[u];
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to;if(v==fa[u])continue;
			fa[v]=u;son[u].push_back(v);Dfs(v);
			siz[u]+=siz[v];
			dis[u]+=dis[v]+siz[v];
		}
	}
	void Dp(int u){
		if(fa[u]){
			ans+=min(siz[u],2*k-siz[u]);
		}
		for(auto v : son[u])Dp(v);
	}
}G;

int spe[maxn];

void solve(){
	cin>>n>>k>>tpy;int x,y;
	Rep(i,1,2*k)cin>>spe[i],G.val[spe[i]]=1;
	Rep(i,2,n)cin>>x>>y,G.lqx(x,y),G.lqx(y,x);
	G.Dfs(1);G.Dp(1);
	cout<<ans<<"\n";
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }


标签:pre,int,siz,9.15,long,maxn,开小灶,CSP,define
来源: https://www.cnblogs.com/Delov/p/16698060.html