最近做题遇到的坑·三 cf429c,cf377d,cf288d,cf294e,cf958b2,cf765e,cf812e,gym101981d,gym101981g

cf429c

给一个序列 a 1 . . a n a_1..a_n a1..an,能否找到一棵n个节点的,且每个非叶子节点有至少2个叶子的树,使每个节点子树大小一一对应于序列a中的一个数
n<=24

暴力都不会写。。直接搞搞就好了

#include<bits/stdc++.h>
using namespace std;
int n,a[25],tot;
int st[12],top;
void dfs(int d) {
	if(d>tot) cout<<"YES",exit(0);
	for(int i=1;i<=top;++i) {
		if(st[i]>=a[d]&&(st[i]>a[d]||st[i]!=a[i]-1)) {
			st[i]-=a[d];
			st[++top]=a[d]-1;
			dfs(d+1);
			st[i]+=a[d];
			--top;
		}
	}
}
int main() {
	cin>>n;
	for(int i=1;i<=n;++i) {
		cin>>a[i];
		if(a[i]==2) return cout<<"NO",0;
		if(a[i]>1) ++tot;
	}
	sort(a+1,a+n+1,greater<int>());
	if(a[1]!=n) return cout<<"NO",0;
	st[top=1]=a[1]-1;
	dfs(2);
	cout<<"NO";
}

cf377d

n个人,每个人有值 l i ≤ v i ≤ r i l_i\le v_i\le r_i liviri,要选一些人是人数最多,满足选中的人中 m a x { v i } ≤ m i n { r i } , m i n { v i } ≥ m a x { l i } max\{v_i\}\le min\{r_i\},\quad min\{v_i\}\ge max\{l_i\} max{vi}min{ri},min{vi}max{li}
n<=1e5

想不到,,看题解:搞一个公共边界L,R,
则要满足l_i<=L<=v_i, v_i<=R<=r_i,
可以看成二维的一个矩形区域,求n个矩形最多的重叠次数(怎么想到的呢

cf288d

求一棵树中有多少对不相交的链

菜如我,往往会把简单的题意讨论的极为复杂。。
我试图分类讨论两条链交的是人型的链、丿型的链、点,点又分四叉、三叉、两叉,然后@#¥%……&#。。。
只要看对每个点u,以它为最高点的所有链,要么与 同样以u为最高点的链 相交,要么与最高点在u的子树外,且另一端点在u的子树内的链 相交
这样就把所有情况不重不漏算了一次

//贺!
#include<bits/stdc++.h>
using namespace std;
const int maxn = 8e4+6;
int n;
long long s[maxn];
long long ans = 0;
vector<int> E[maxn];
void dfs(int x,int f){
    s[x]=1;
    long long tmp = 0;
    for(int i=0;i<E[x].size();i++){
        int v=E[x][i];
        if(v==f)continue;
        dfs(v,x);
        tmp+=1ll*s[x]*s[v];
        s[x]+=s[v];
    }
    ans-=1ll*tmp*(tmp+2LL*s[x]*(n-s[x]));
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        E[a].push_back(b);
        E[b].push_back(a);
    }
    ans=1ll*n*(n-1LL)/2LL*(n*(n-1LL)/2LL);
    dfs(1,0);
    cout<<ans<<endl;
}

cf294e

一棵边带权树,进行一次操作,删一条边,再把这条边加到任意两个点之间,使树仍是树。
求操作后所有链的权值和的最小值
n<=5000

果然不出所料,xjb dfs就行了,就是写的太慢了

#pragma GCC optimize("inline",2)
#include<bits/stdc++.h>
#define FOR(i,s,t) for(int i=s;i<=t;++i)
#define REP(i,t,s) for(int i=t;i>=s;--i)
#define RESET(a) memset(a,0,sizeof a)
#define int long long
using namespace std;
typedef long long LL;
const int N=5005;
const LL inf=1e18;
int n,hd[N],nxt[N<<1],to[N<<1],w[N<<1],tot=1;
int sz[N<<1];
LL aa[N<<1],eg[N<<1];
void add(int a,int b,int c) {
	nxt[++tot]=hd[a],to[tot]=b,w[tot]=c;
	hd[a]=tot;
}
LL dfs1(int u,int fe,LL d) {
	LL re=d;
	for(int i=hd[u];i;i=nxt[i])
		if(i!=fe) re+=dfs1(to[i],i^1,d+w[i]);
	return eg[fe]=re;
}
int dfs2(int u,int fe) {
	int re=1;
	for(int i=hd[u];i;i=nxt[i])
		if(i!=fe) re+=dfs2(to[i],i^1);
	sz[fe^1]=n-re;
	return sz[fe]=re;
}
void solve(int rt) {
	RESET(eg);
	LL sum=dfs1(rt,0,0);
	FOR(i,2,tot) aa[i]=min(aa[i],sum-eg[i]);
}
int zz[N];
LL wi[N],wo[N],bb[N<<1];
void dfs3(int u,int fe) {
	zz[u]=1;
	wi[u]=0,wo[u]=0;
	for(int i=hd[u];i;i=nxt[i]) if(i!=fe) {
		dfs3(to[i],i^1);
		zz[u]+=zz[to[i]];
		wi[u]+=wi[to[i]];
		wo[u]+=wo[to[i]]+zz[to[i]]*w[i];
		wi[u]-=(wo[to[i]]+zz[to[i]]*w[i])*zz[to[i]];
	}
	wi[u]+=wo[u]*zz[u];
	bb[fe]=wi[u];
}
signed main() {
	memset(aa,0x3f,sizeof aa);
	cin>>n;
	FOR(i,1,n-1) {
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
		add(y,x,z);
	}
	FOR(i,1,n) dfs3(i,0);
	FOR(i,1,n) solve(i);
	dfs2(1,0);
	LL ans=1e18;
	FOR(i,1,n-1) {
		int r=i<<1,s=i<<1|1;
		ans=min(ans,1LL*w[r]*sz[r]*sz[s]+aa[r]*sz[r]+aa[s]*sz[s]
			+bb[r]+bb[s]);
	}
	cout<<ans<<endl;
}

cf958b2

一棵树,求在树上标记1,2,…,n个点分别最多能占领多少个节点
若一个节点在两个被标记节点的路径上,则它被占领

果然先找个直径的端点就行 然后一直选 能使当前的覆盖增加最多的叶子就行了
我还写了个线段树,太蠢了

#pragma GCC optimize("inline",2)
#include<bits/stdc++.h>
#define FOR(i,s,t) for(int i=s;i<=t;++i)
#define REP(i,t,s) for(int i=t;i>=s;--i)
#define reset(a) memset(a,0,sizeof a)
using namespace std;
const int N=1e5+5;
int n,fa[N],st[N],ed[N],pos[N],dep[N],vi[N],Q[N],rt,cnt;
int mx[N<<2],mk[N<<2];
vector<int> e[N];
void getrt() {
	int L=0,R=1; Q[1]=1;
	while(L<R) {
		int u=Q[++L];
		for(auto v:e[u]) if(!vi[v]) {
			vi[v]=1;
			Q[++R]=v;
		}
	}
	reset(vi);
	rt=Q[R];
}
void dfs(int u,int f,int d) {
	fa[u]=f,dep[u]=d,pos[st[u]=++cnt]=u;
	for(auto v:e[u]) if(v!=f) {
		dfs(v,u,d+1);
	}
	ed[u]=cnt;
}
void pushup(int u) {
	mx[u]=max(mx[u<<1],mx[u<<1|1]);
}
void pushdn(int u) {
	mk[u<<1]+=mk[u];
	mk[u<<1|1]+=mk[u];
	mx[u<<1]+=mk[u];
	mx[u<<1|1]+=mk[u];
	mk[u]=0;
}
void build(int u,int l,int r) {
	if(l==r) mx[u]=dep[pos[l]]-1;
	else {
		int mi=l+r>>1;
		build(u<<1,l,mi);
		build(u<<1|1,mi+1,r);
		pushup(u);
	}
}
void upd(int u,int l,int r,int L,int R,int w) {
	if(L<=l&&r<=R) mx[u]+=w,mk[u]+=w;
	else {
		pushdn(u);
		int mi=l+r>>1;
		if(L<=mi) upd(u<<1,l,mi,L,R,w);
		if(R>mi) upd(u<<1|1,mi+1,r,L,R,w);
		pushup(u);
	}
}
pair<int,int> qry(int u,int l,int r) {
	if(l==r) return make_pair(mx[u],l);
	pushdn(u);
	int mi=l+r>>1;
	if(mx[u<<1]>mx[u<<1|1]) return qry(u<<1,l,mi);
	return qry(u<<1|1,mi+1,r);
}
void modi(int u) {
	if(vi[u]) return;
	vi[u]=1;
	upd(1,1,n,st[u],ed[u],-1);
	modi(fa[u]);
}
int main() {
	cin>>n;
	FOR(i,1,n-1) {
		int x,y;
		cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	getrt();
	dfs(rt,0,1);
	build(1,1,n);
	vi[rt]=1;
	int ans=1;
	cout<<ans;
	FOR(i,2,n) {
		auto pr=qry(1,1,n);
		ans+=pr.first;
		cout<<' '<<ans;
		modi(pos[pr.second]);
	}
}
#include<bits/stdc++.h>
#define FOR(i,s,t) for(int i=s;i<=t;++i)
#define REP(i,t,s) for(int i=t;i>=s;--i)
#define RESET(a) memset(a,0,sizeof a)
using namespace std;
typedef long long LL;
const int N=1e5+5;
vector<int> e[N],b;
int n,dep[N],mxd[N],rt;

void dfs(int u,int f,int d) {
	mxd[u]=dep[u]=d;
	for(auto v:e[u]) if(v!=f) {
		dfs(v,u,d+1);
		mxd[u]=max(mxd[u],mxd[v]);
	}
}

void dfs2(int u,int f,int d) {
	if((f||n==1)&&e[u].size()==1) b.push_back(d);
	int s=-1;
	for(auto v:e[u]) if(v!=f) {
		if(s==-1||mxd[s]<mxd[v]) s=v;
	}
	if(~s) dfs2(s,u,d+1);
	for(auto v:e[u]) if(v!=f&&v!=s) dfs2(v,u,1);
}
int main() {
	cin>>n;
	FOR(i,1,n-1) {
		int x,y;
		cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	dfs(1,0,1);
	FOR(i,1,n) if(dep[rt]<dep[i]) rt=i;
	dfs(rt,0,1);
	dfs2(rt,0,1);
	sort(b.begin(),b.end(),greater<int>());
	int ans=0;
	cout<<1;
	FOR(i,0,n-2) {
		if(i<b.size()) ans+=b[i];
		cout<<' '<<ans;
	}
}

cf765e
在这里插入图片描述

一棵树,如图的链可以合并,问能不能合并,合并的最短长度

果然是直接找直径的中点,然后以它为根看看能不能合并就好了
合并成一条链后如果长度是偶数还要继续对折。。

#include<bits/stdc++.h>
#define FOR(i,s,t) for(int i=s;i<=t;++i)
#define REP(i,t,s) for(int i=t;i>=s;--i)
using namespace std;
typedef long long LL;
const int N=2e5+5;
int n,dep[N],fa[N];
vector<int> e[N];

void dfs(int u,int f,int d) {
	fa[u]=f; dep[u]=d;
	for(auto v:e[u]) if(v!=f) dfs(v,u,d+1);
}
int solve(int u,int f) {
	int re=0,re2=0;
	for(auto v:e[u]) if(v!=f) {
		int t=solve(v,u);
		if(t==-1) return -1;
		if(!re) re=t;
		else if(re!=t) {
			if(!f&&(!re2||re2==t)) re2=t;
			else return -1;
		}
	}
	if(!f) return re+re2;
	return re+1;
}
int main() {
	int ans=1e9,t;
	cin>>n;
	FOR(i,1,n-1) {
		int x,y;
		cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	dfs(1,0,1);
	int u1=1;
	FOR(i,2,n) if(dep[u1]<dep[i]) u1=i;
	
	dfs(u1,0,1);
	int u2=1;
	FOR(i,2,n) if(dep[u2]<dep[i]) u2=i;
	
	int p=u2;
	while(dep[p]>(dep[u1]+dep[u2]+1)/2) p=fa[p];
	
	if((t=solve(p,0))!=-1) ans=min(ans,t);
	
	if(fa[p]) {
		p=fa[p];
		if((t=solve(p,0))!=-1) ans=min(ans,t);
	}
	if(ans==1e9) return cout<<-1,0;
	
	while(ans&1^1) ans>>=1;

	cout<< (ans<1e9 ? ans : -1) << endl;
}

cf812e

树上博弈,如何如何。。

https://blog.csdn.net/weixin_34151004/article/details/94687901
就是可以分奇偶层,只有偶数层有效

//he!
#include<bits/stdc++.h>
#define FOR(i,s,t) for(int i=s;i<=t;++i)
#define REP(i,t,s) for(int i=t;i>=s;--i)
using namespace std;
typedef long long LL;
const int N=1e5+5;
int n,a[N],dep[N],D,S,C0,C1;
int bb[N*200];
vector<int> e[N];
void dfs(int u,int d) {
	dep[u]=d;
	if(e[u].empty()) D=d&1;
	for(auto v:e[u]) dfs(v,d^1);
}
int main() {
	cin>>n;
	FOR(i,1,n) cin>>a[i];
	int x;
	FOR(i,2,n) cin>>x,e[x].push_back(i);
	dfs(1,0);
	FOR(i,1,n)
		if(dep[i]==D) S^=a[i],++C0;
		else ++C1;
	LL ans=S ? 0 : 1LL*C0*(C0-1)/2+1LL*C1*(C1-1)/2;
	FOR(i,1,n) if(dep[i]==D) ++bb[S^a[i]];
	FOR(i,1,n) if(dep[i]!=D) ans+=bb[a[i]];
	cout<<ans<<endl;
}

2018ICPC南京D题 Country Meow

最小球覆盖?倒是先看出要用模拟退火 不会,就直接随机贪心
学会了,随机贪心要多rand几次哦

#include<bits/stdc++.h>
#define FOR(i,s,t) for(int i=s;i<=t;++i)
#define REP(i,t,s) for(int i=t;i>=s;--i)
using namespace std;
typedef long long LL;
struct Node {
	double x,y,z;
	Node operator-(Node v) {
		return (Node){x-v.x,y-v.y,z-v.z};
	}
	double operator~() {
		return sqrt(x*x+y*y+z*z);
	}
} a[105];
const double eps=1e-8,delta=0.5;
int n;
Node bst; double ans=1e18;
double qry(Node u) {
	double re=0;
	FOR(i,1,n) re=max(re,~(u-a[i]));
	return re;
}
void chk(double x,double y,double z) {
//	if(rand()&1) return;
	Node u=(Node){x,y,z};
	double cur=qry(u);
	if(cur<ans) {
		bst=u,ans=cur;
//		cerr << "(" << bst.x << ' ' << bst.y << ' ' << bst.z << "): " 
//		 	<< ans << endl;
	}
}
double solve() {
	bst=(Node){rand(),rand(),rand()};
	ans=qry(bst);
	Node &u=bst;
	for(double p=2e5;p>eps;p*=delta) {
		chk(u.x,u.y,u.z+p);
		chk(u.x,u.y,u.z-p);
		chk(u.x,u.y+p,u.z);
		chk(u.x,u.y-p,u.z);
		chk(u.x+p,u.y,u.z);
		chk(u.x-p,u.y,u.z);
	}
}
int main() {
	double aaa=1e18;
	cin>>n;
	FOR(i,1,n) cin>>a[i].x>>a[i].y>>a[i].z;
	int clc=clock();
	while(clock()-clc<CLOCKS_PER_SEC) solve(),aaa=min(ans,aaa);
	printf("%.10lf\n",aaa);
}

南京 G

数数题,分类数就好了。要么分奇偶然后打出表来插值也行

#include<bits/stdc++.h>
#define FOR(i,s,t) for(int i=s;i<=t;++i)
#define REP(i,t,s) for(int i=t;i>=s;--i)
#define int long long
using namespace std;
typedef long long LL;
namespace IO {
	char buf[1<<22],*p1=buf,*p2=buf;
	#define gc (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin))==p1?EOF:*p1++)
	//#define gc getchar()
	inline int rd() {
		int x=0; char c; while(isspace(c=gc));
		do x=x*10+(c^48); while(isdigit(c=gc));
		return x;
	}
} using IO::rd;
const int M=1e9+7;
const int I2=500000004,I3=333333336,I4=250000002,I6=166666668;
//#define M (LL)(1e18)
LL p1(LL x) {
	if(x<=0) return 0;
	return x*(x+1)/2%M;
}
LL p2(LL x) {
	if(x<=0) return 0;
	return x*(x+1)%M*(2*x+1)%M*I6%M;
}
LL p3(LL x) {
	if(x<=0) return 0;
	return 1LL*x*x%M*(x+1)%M*(x+1)%M*I4%M;
}
signed main() {
	int t,n;
	t=rd();
	FOR(tt,1,t) {
		n=rd();
//		n=tt;
		LL ans=(p2(n)+p1(n))*I2%M;
		if(n&1) {
			int k=n-2+1>>1;
			(ans+=(8*p3(k)%M+p1(k))*I3%M-2*p2(k)+2*p2(k)-p1(k))%=M;
		
		} else {
			int k=n-2>>1;
			(ans+=(8*p3(k)%M+p1(k))*I3%M+2*p2(k)+2*p2(k)+p1(k))%=M;
		}
	
		if(n&1) {
			n/=2;
			ans+=2*p2(n)+p1(n);
		}
		else {
			n/=2;
			ans+=2*p2(n)-p1(n);
		}
		printf("%lld\n",(ans%M+M)%M);
	}
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值