1. 程式人生 > 其它 >4.1省選模擬

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\)的下降子序列就不合法

#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;
}