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:
1
≤
N
≤
200
,
1
≤
K
≤
1
0
5
,
1
≤
D
i
≤
1
0
5
1 ≤ N ≤ 200, 1 ≤ K ≤ 10^5, 1 ≤ Di ≤ 10^5
1≤N≤200,1≤K≤105,1≤Di≤105
题解:
我的做法:
显然子树中的代价与
u
u
u的最近点有关,那么设
f
[
u
]
[
i
]
f[u][i]
f[u][i]表示
u
u
u的最近点在子树外,且距离为
i
i
i时子树中的最小代价;
g
[
u
]
[
i
]
g[u][i]
g[u][i]表示
u
u
u的最近点在子树内,且距离为
i
i
i时子树中的最小代价。记录
G
[
u
]
G[u]
G[u]表示
u
u
u子树内的点都在子树内解决的最小代价,最后答案就是
G
[
1
]
G[1]
G[1]。转移
g
g
g时要枚举
u
u
u的最近点在哪个子树,总复杂度
O
(
n
3
)
O(n^3)
O(n3)。
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:
题意:
n
n
n个点的树,每个点有颜色
T
i
T_i
Ti,
Q
Q
Q次操作,修改一个点的颜色或查询
u
u
u到
v
v
v路径上的第
t
t
t种颜色的个数。
1
≤
N
≤
100000
,
1
≤
Q
≤
200000
,
0
≤
T
i
<
2
31
1 ≤ N ≤ 100000, 1 ≤ Q ≤ 200000, 0 ≤ Ti < 2^{31}
1≤N≤100000,1≤Q≤200000,0≤Ti<231
题解:
给每个颜色开个线段树。
动态开点线段树+树剖
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)。
可以不用树剖,因为是数个数有可减性,变成两条到根的链去掉LCA到根的两倍。到根的路径就可以子树修改单点查询了,
O
(
n
l
o
g
n
)
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);
}
}
}