4.1省选模拟
\(T1\)
\(dis(x,y)=dep(x)+dep(y)-2\times dep(lca(x,y))\)
显然重心最优
然后做一个匹配即可
#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define int long long
#define MAXM 200005
#define MAXN 200005
using namespace std;
int head[MAXN],nxt[MAXN],val[MAXN],siz[MAXN],to[MAXN],tot;
int Max[MAXN],Ans,rt,n;
void add(int u,int v,int w)
{
tot++;
to[tot]=v;
val[tot]=w;
nxt[tot]=head[u];
head[u]=tot;
}
void dfs_rt(int now,int fa)
{
siz[now]=1;
for(int i=head[now];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
dfs_rt(y,now);
siz[now]+=siz[y];
Max[now]=max(Max[now],siz[y]);
}
Max[now]=max(Max[now],n-siz[now]);
if(Max[now]<Max[rt]) rt=now;
}
void dfs(int now,int fa,int dep)
{
Ans+=2*dep;
for(int i=head[now];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
dfs(y,now,dep+val[i]);
}
}
int vis[MAXN],Sum[MAXN];
set<int> In[MAXN];
set<int> Min;
set<pair<int,int> > Set;
void dfs_vis(int now,int fa,int fg)
{
siz[fg]++;
In[fg].insert(now);
vis[now]=fg;
for(int i=head[now];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
dfs_vis(y,now,fg);
}
}
void Link(int x,int y)
{
// cout<<"Link: "<<x<<" "<<y<<endl;
int p=vis[x],q=vis[y];
Min.erase(y);
if(p)
{
Set.erase(make_pair(Sum[p],p));
Set.insert(make_pair(--Sum[p],p));
}
if(q)
{
In[q].erase(y);
if(!In[q].empty()) Min.insert(*In[q].begin());
Set.erase(make_pair(Sum[q],q));
Set.insert(make_pair(--Sum[q],q));
}
}
int sol(int x)
{
int res;
if(Set.rbegin()->first==n-x+1&&Set.rbegin()->second!=vis[x])
{
res=*In[Set.rbegin()->second].begin();
}
else
{
res=vis[*Min.begin()]!=vis[x]||x==rt?*Min.begin():*next(Min.begin());
}
Link(x,res);
return res;
}
signed main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%lld",&n);
for(int i=1,u,v,w;i<n;i++)
{
scanf("%lld%lld%lld",&u,&v,&w);
add(u,v,w); add(v,u,w);
}
Max[0]=n;
dfs_rt(1,1);
dfs(rt,rt,0);
cout<<Ans<<"\n";
if(n==1) {
puts("1");
return 0;
}
Min.insert(rt);
for(int i=head[rt];i;i=nxt[i])
{
int y=to[i];
siz[y]=0;
dfs_vis(y,rt,y);
Min.insert(*In[y].begin());
Set.insert(make_pair(Sum[y]=siz[y]*2,y));
}
for(int i=1;i<=n;i++)
{
cout<<sol(i)<<" ";
}
}
\(T2\)
诚不欺我,果然有\(dp\)
\(k<=12\)可以状压,\(m<=4\)那么状态转移数目较少
那么转移易得
\(dp[i][j][S]\)表示我们目前填第\(i\)个数字,已经填了\(j\)个位置,目前大于等于\(i-m\)的位置有哪些的方案数
由于我们是递增填数,就可以实时更新,又因为每个数不同,那么记录一下哪些在原序列里就好了
#include <bits/stdc++.h>
#define mod 1000000007
#define MAXN 205
using namespace std;
int n,k,m,Sum;
struct Mat
{
int jz[MAXN][MAXN];
Mat operator * (Mat x)
{
Mat res;
memset(res.jz,0,sizeof(res.jz));
for(int i=0;i<=Sum;i++)
{
for(int j=0;j<=Sum;j++)
{
for(int k=0;k<=Sum;k++)
{
res.jz[i][j]=(res.jz[i][j]+1ll*jz[i][k]*x.jz[k][j])%mod;
}
}
}
return res;
}
}b,St;
int main()
{
cin>>n>>k>>m;
Sum=k<<m;
for(int i=0;i<k;i++)
{
for(int j=0;j<(1<<m);j++)
{
int Sit=(j<<1)&(1 << m)-1;
int num=1+__builtin_popcount(j);
b.jz[(i<<m)+j][(i<<m)+Sit]=1;
if(i==k-1) b.jz[(i<<m)+j][Sum]=num;
else b.jz[(i<<m)+j][(i+1<<m)+Sit+1]=num;
}
}
b.jz[Sum][Sum]=St.jz[0][0]=1;
while(n)
{
if(n&1) St=St*b;
b=b*b;
n>>=1;
}
cout<<St.jz[0][Sum]<<endl;
return 0;
}
\(T3\)
大家都会猜结论\(QAQ,\)谁能教教我怎么猜中啊
考虑交换的中心点必然\(p_i=i\)
然后把\(p_i=i\)去掉,分为若干区间,区间内部值域相等
若出现超过长度为\(3\)的下降子序列就不合法
缩进寄了dk
#include<bits/stdc++.h>
#define MAXN 600005
using namespace std;
int n,p[MAXN],a[MAXN],Min[MAXN];
int stk[MAXN],top;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&p[i]);
}
p[n+1]=n+1,p[0]=0;
for(int i=1;i<=n;i++)
{
if(p[i-1]!=i-1&&p[i]!=i&&p[i+1]!=i+1)
{
puts("No");
return 0;
}
a[i]=(i==p[i]);
}
for(int i=1,j;i<=n;i=j+1)
{
for(j=i;j<n&&a[j]!=a[j+1];j++);
top=0;
for (int k=i;k<=j;k++)
{
if(p[k]<i||j<p[k])
{
puts("No");
return 0;
}
if(!a[k]) stk[++top]=p[k];
}
Min[top]=stk[top];
for(int k=top-1;k>=1;k--)
{
Min[k]=min(Min[k+1],stk[k]);
}
int Max=stk[1];
for(int k=2;k<top;k++)
{
Max=max(Max,stk[k]);
if(Min[k]<stk[k]&&stk[k]<Max)
{
puts("No");
return 0;
}
}
}
puts("Yes");
return 0;
}