模拟赛20200217【删点判断链状(适当枚举的分类讨论),到最近点的距离和(树形DP),路径某种颜色个数(动态开点线段树)】

T1:

在这里插入图片描述
1 ≤ N ≤ 100000, 1 ≤ M ≤ 2N

题解:

在这里插入图片描述
转化条件:一个图的每个连通块为链,等价于每个点的度数小于等于 2 且无环. 转化后的条件明显更有利于解决问题。
容易想到当一个点的度数==3时必然是删它或周围的点,>3时就是必删点,成环那么删去环上的某个点,我在考试的时候采用了将这些点打上idx的标记,那么要删的点就是集齐了所有idx的点(Hash实现),但是环没办法快速处理。
在这里插入图片描述
这启示我们当分类讨论将选择范围缩小为常数级别时就可以尝试枚举了。

Code:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m;
bool flg_deg3;
vector<int>G[maxn];
vector<pair<int,int> >E;
struct Graph{
	int F[maxn],siz[maxn],cir,cirlen,node_deg3,deg[maxn],del;
	Graph(){fill(siz+1,siz+maxn,1);}
	int find(int x){return !F[x]?x:F[x]=find(F[x]);}
	bool merge(int x,int y){
		if((x=find(x))==(y=find(y))) return 0;
		if(siz[x]<siz[y]) swap(x,y);
		F[y]=x,siz[x]+=siz[y];
		return 1;
	}
	void insert(int x,int y){
		if(x==del||y==del) return;
		if(++deg[x]==3) node_deg3=x;
		if(++deg[y]==3) node_deg3=y;
		if(!merge(x,y)) cir++,cirlen=siz[find(x)];
	}
	bool valid(){return !node_deg3&&!cir;}
}f,g[4];
int main()
{
	freopen("chain.in","r",stdin);
	freopen("chain.out","w",stdout);
	read(n),read(m);
	int x,y; char op;
	while(m--){
		while(!isalpha(op=getc()));
		if(op=='Q'){
			if(flg_deg3){
				int s=0;
				for(int i=0;i<4;i++) s+=g[i].valid();
				printf("%d\n",s);
			}
			else x=f.cir,printf("%d\n",!x?n:x==1?f.cirlen:0);
		}
		else{
			read(x),read(y);
			if(flg_deg3) for(int i=0;i<4;i++) g[i].insert(x,y);
			else{
				G[x].push_back(y),G[y].push_back(x);
				E.push_back(make_pair(x,y));
				f.insert(x,y);
				if(x=f.node_deg3){
					flg_deg3=1,g[3].del=x;
					for(int i=0;i<3;i++) g[i].del=G[x][i];
					for(int i=0;i<4;i++)
						for(int j=0,lim=E.size();j<lim;j++)
							g[i].insert(E[j].first,E[j].second);
				}
			}
		}
	}
}

T2:

在这里插入图片描述
1N200,1K105,1Di1051 ≤ N ≤ 200, 1 ≤ K ≤ 10^5, 1 ≤ Di ≤ 10^5

题解:

我的做法:
显然子树中的代价与uu的最近点有关,那么设f[u][i]f[u][i]表示uu的最近点在子树外,且距离为ii时子树中的最小代价;g[u][i]g[u][i]表示uu的最近点在子树内,且距离为ii时子树中的最小代价。记录G[u]G[u]表示uu子树内的点都在子树内解决的最小代价,最后答案就是G[1]G[1]。转移gg时要枚举uu的最近点在哪个子树,总复杂度O(n3)O(n^3)

STD:
在这里插入图片描述
Code1:

#include<bits/stdc++.h>
#define maxn 205
#define rep(i,v) for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff)
using namespace std;
int n,K,D[maxn],mxd[maxn],f[maxn][maxn],g[maxn][maxn],G[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
void dfs(int u,int ff){
	rep(i,v) dfs(v,u),mxd[u]=max(mxd[u],mxd[v]+1);
	for(int i=1;i<n;i++){
		f[u][i]=D[i];
		rep(j,v) f[u][i]+=min(G[v],f[v][i+1]);
	}
	g[u][0]=K;
	rep(i,v) g[u][0]+=min(G[v],f[v][1]);
	for(int i=1;i<=mxd[u];i++){
		rep(j,x){
			int ret=g[x][i-1]+D[i];
			rep(k,v) if(v!=x) ret+=min(G[v],f[v][i+1]);
			g[u][i]=min(g[u][i],ret);
		}
	}
	for(int i=0;i<=mxd[u];i++) G[u]=min(G[u],g[u][i]);
}
int main()
{
	freopen("logistics.in","r",stdin);
	freopen("logistics.out","w",stdout);
	memset(f,0x3f,sizeof f),memset(g,0x3f,sizeof g),memset(G,0x3f,sizeof G);
	scanf("%d%d",&n,&K);
	for(int i=1;i<n;i++) scanf("%d",&D[i]);
	for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
	dfs(1,0);
	printf("%d\n",G[1]);
}

Code2:

#include <cstdio>
#include <cstring>
#include <cstdlib>

using namespace std;

enum {MAXN = 2010,MAXE = MAXN * 2,INF = 1000000000};
int cost[MAXN],dist[MAXN][MAXN],dp[MAXN][MAXN];

int map[MAXN],end[MAXE],nxt[MAXE];
int tot = 0;
void add(int u,int v)
{
  end[tot] = v;
  nxt[tot] = map[u];
  map[u] = tot++;
}

void dfs_dist(int u,int fath,int *dist)
{
  if (fath) dist[u] = dist[fath] + 1;
  else dist[u] = 0;
  for(int p = map[u];p != -1;p = nxt[p])
    if (end[p] != fath) dfs_dist(end[p],u,dist);
}

int N,K;
void dfs(int u,int fath)
{
  for(int r = 1;r <= N;r++) dp[u][r] = cost[dist[u][r]];

  for(int p = map[u];p != -1;p = nxt[p])
    if (end[p] != fath)
    {
      const int &v = end[p];
      dfs(v,u);

      for(int r = 1;r <= N;r++)
	{
	  int minv = dp[v][r];
	  for(int r2 = 1;r2 <= N;r2++)
	    if (dp[v][r2] + K < minv)
	      {
		minv = dp[v][r2] + K;
	      }
	  dp[u][r] += minv;
	}
    }
}

int main()
{
  freopen("logistics.in","r",stdin);
  freopen("logistics.out","w",stdout);

  int n,k;
  scanf("%d%d",&n,&k);
  cost[0] = 0;
  for(int i = 1;i < n;i++) scanf("%d",&cost[i]);

  memset(map,-1,sizeof(map));
  int u,v;
  for(int i = 0;i < n-1;i++)
    {
      scanf("%d%d",&u,&v);
      add(u,v),add(v,u);
    }

  for(int i = 1;i <= n;i++) dfs_dist(i,0,dist[i]);
  N = n,K = k;
  dfs(1,0);
  
  int minans = INF,minr = 0;
  for(int r = 1;r <= N;r++)
    if (dp[1][r] < minans)
      {
	minans = dp[1][r];
	minr = r;
      }
  minans += k;

  printf("%d\n",minans);
  return 0;
}


T3:

题意:

nn个点的树,每个点有颜色TiT_iQQ次操作,修改一个点的颜色或查询uuvv路径上的第tt种颜色的个数。
1N100000,1Q200000,0Ti<2311 ≤ N ≤ 100000, 1 ≤ Q ≤ 200000, 0 ≤ Ti < 2^{31}

题解:

给每个颜色开个线段树。
动态开点线段树+树剖 O(nlog2n)O(nlog^2n)
可以不用树剖,因为是数个数有可减性,变成两条到根的链去掉LCA到根的两倍。到根的路径就可以子树修改单点查询了,O(nlogn)O(nlogn)
Code:

#include<bits/stdc++.h>
#define maxn 300005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,Q,a[maxn],rt[maxn],lc[maxn*20],rc[maxn*20],s[maxn*20],sz,ans;
int fa[maxn],dep[maxn],son[maxn],siz[maxn],top[maxn],dfn[maxn],tim;
map<int,int>id;
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
inline int turn(int x){int &y=id[x]; if(!y) y=++m; return y;}
void dfs1(int u,int ff){
	dep[u]=dep[fa[u]=ff]+1,siz[u]=1;
	for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
		dfs1(v,u),siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int tp){
	top[u]=tp,dfn[u]=++tim;
	if(son[u]) dfs2(son[u],tp);
	for(int i=fir[u],v;i;i=nxt[i]) if(!dfn[v=to[i]]) dfs2(v,v);
}
void insert(int &i,int l,int r,int x,int d){
	if(!i) i=++sz;
	s[i]+=d;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(x<=mid) insert(lc[i],l,mid,x,d);
	else insert(rc[i],mid+1,r,x,d);
}
int query(int i,int l,int r,int x,int y){
	if(!i||!s[i]) return 0;
	if(x<=l&&r<=y) return s[i];
	int mid=(l+r)>>1,ret=0;
	if(x<=mid) ret+=query(lc[i],l,mid,x,y);
	if(y>mid) ret+=query(rc[i],mid+1,r,x,y);
	return ret;
}
void solve(int u,int v,int t){
	ans=0;
	for(;top[u]!=top[v];u=fa[top[u]]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		ans+=query(rt[t],1,n,dfn[top[u]],dfn[u]);
	}
	if(dep[u]<dep[v]) swap(u,v);
	ans+=query(rt[t],1,n,dfn[v],dfn[u]);
	printf("%d\n",ans);
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	int x,y,t; char op;
	read(n),read(Q);
	for(int i=1;i<=n;i++) read(a[i]),a[i]=turn(a[i]);
	for(int i=1;i<n;i++) read(x),read(y),line(x,y),line(y,x);
	dfs1(1,0),dfs2(1,1);
	for(int i=1;i<=n;i++) insert(rt[a[i]],1,n,dfn[i],1);
	while(Q--){
		while(!isalpha(op=getc()));
		if(op=='C'){
			read(x),read(t),x^=ans,t=turn(t^ans);
			insert(rt[a[x]],1,n,dfn[x],-1),insert(rt[a[x]=t],1,n,dfn[x],1);
		}
		else{
			read(x),read(y),read(t),x^=ans,y^=ans,t=turn(t^ans);
			solve(x,y,t);
		}
	}
}
发布了381 篇原创文章 · 获赞 139 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章