给一个序列 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";
}
n个人,每个人有值 l i ≤ v i ≤ r i l_i\le v_i\le r_i li≤vi≤ri,要选一些人是人数最多,满足选中的人中 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个矩形最多的重叠次数(怎么想到的呢
求一棵树中有多少对不相交的链
菜如我,往往会把简单的题意讨论的极为复杂。。
我试图分类讨论两条链交的是人型的链、丿型的链、点,点又分四叉、三叉、两叉,然后@#¥%……&#。。。
只要看对每个点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;
}
一棵边带权树,进行一次操作,删一条边,再把这条边加到任意两个点之间,使树仍是树。
求操作后所有链的权值和的最小值
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;
}
一棵树,求在树上标记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;
}
}
一棵树,如图的链可以合并,问能不能合并,合并的最短长度
果然是直接找直径的中点,然后以它为根看看能不能合并就好了
合并成一条链后如果长度是偶数还要继续对折。。
#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;
}
树上博弈,如何如何。。
https://blog.youkuaiyun.com/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;
}
最小球覆盖?倒是先看出要用模拟退火 不会,就直接随机贪心
学会了,随机贪心要多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);
}
数数题,分类数就好了。要么分奇偶然后打出表来插值也行
#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);
}
}