模擬賽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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章