题目名好像都是童话故事
T1
首先,dead line 是一条直线,而不是线段。考试的时候一直以为是线段,那么横竖共有n+m条,考虑斜着的,斜着的交点为有穷的,则需要满足斜率不同,那么只需要统计一边的,再乘2就好了,显然gcd=1时统计答案,则有,
然后有60pts的好成绩,如何优化,考虑维护二维前缀和,设 \(gcd_{i,j}\) 表示到i,j时,gcd为1的数量,设 \(sum_{i,j}\) 表示到i,j斜线的数量,则有,
#include<cstdio>
#define MAX 4000
#define re register
namespace OMA
{
int t,n[MAX+1],m[MAX+1];
int sum[MAX+1][MAX+1];
int gcd[MAX+1][MAX+1];
const int p = 1073741824;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline int get(int a,int b)
{ return b?get(b,a%b):a; }
inline int max(int a,int b)
{ return a>b?a:b; }
signed main()
{
t = read();
for(re int i=1; i<=t; i++)
{
n[0] = max(n[0],n[i] = read());
m[0] = max(m[0],m[i] = read());
}
for(re int i=1; i<=n[0]; i++)
{
for(re int j=1; j<=m[0]; j++)
{
gcd[i][j] = (gcd[i-1][j]+gcd[i][j-1]-gcd[i-1][j-1]+(get(i,j)==1))%p;
sum[i][j] = ((sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+gcd[i][j]-gcd[i>>1][j>>1])%p+p)%p;
}
}
for(re int i=1; i<=t; i++)
{ printf("%d\n",(n[i]+m[i]+sum[n[i]-1][m[i]-1]*2)%p); }
return 0;
}
}
signed main()
{ return OMA::main(); }
T2
考试的时候一眼树剖+线段树,加了个小优化,还能跑出大样列17s,然后MLE爆零,好吧,想骗分还把数组开那么大,MLE也是活该
祭奠一下死去的code。
MLE#include<cstdio>
#include<cstring>
#define MAX 10010
#define re register
#define int long long
namespace OMA
{
int t,n,ans;
struct Graph
{
int next;
int to;
int w;
}edge[MAX<<1];
int ni[MAX][MAX],tot[MAX][MAX];
int cnt=1,head[MAX];
int fa[MAX],son[MAX];
int size[MAX],dep[MAX],w[MAX][2];
int top[MAX],dfn[MAX],id[MAX][2];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline void add(int u,int v,int w)
{
edge[++cnt].next = head[u];
edge[cnt].to = v;
edge[cnt].w = w;
head[u] = cnt;
}
inline void dfs1(int u,int fat,int depth)
{
fa[u] = fat;
size[u] = 1;
dep[u] = depth;
for(re int i=head[u]; i; i=edge[i].next)
{
int v = edge[i].to;
if(v!=fat)
{
dfs1(v,u,depth+1);
size[u] += size[v];
w[v][1] = edge[i].w;
if(!son[u]||size[v]>size[son[u]])
{ son[u] = v; }
}
}
}
inline void dfs2(int u,int t)
{
top[u] = t;
id[dfn[u] = ++cnt][0] = w[u][0];
id[dfn[u]][1] = w[u][1];
if(son[u])
{ dfs2(son[u],t); }
for(re int i=head[u]; i; i=edge[i].next)
{
int v = edge[i].to;
if(v!=fa[u]&&v!=son[u])
{ dfs2(v,v); }
}
}
struct Segment_Tree
{
struct TREE
{
int nim;
int sum;
int l,r;
}st[MAX<<2];
inline int ls(int p)
{ return p<<1; }
inline int rs(int p)
{ return p<<1|1; }
inline int min(int a,int b)
{ return a<b?a:b; }
inline void Push_up(int p)
{
st[p].sum = st[ls(p)].sum+st[rs(p)].sum;
st[p].nim = min(st[ls(p)].nim,st[rs(p)].nim);
}
inline void build(int p,int l,int r)
{
st[p].l = l,st[p].r = r;
if(l==r)
{ st[p].sum = id[l][1],st[p].nim = id[l][0]; return ; }
int mid = (l+r)>>1;
build(ls(p),l,mid),build(rs(p),mid+1,r);
Push_up(p);
}
inline int query1(int p,int l,int r)
{
if(l<=st[p].l&&st[p].r<=r)
{ return st[p].sum; }
int sum = 0,mid = (st[p].l+st[p].r)>>1;
if(l<=mid)
{ sum += query1(ls(p),l,r); }
if(r>mid)
{ sum += query1(rs(p),l,r); }
return sum;
}
inline int query2(int p,int l,int r)
{
if(l<=st[p].l&&st[p].r<=r)
{ return st[p].nim; }
int nim = 0x3f3f3f3f,mid = (st[p].l+st[p].r)>>1;
if(l<=mid)
{ nim = min(nim,query2(ls(p),l,r)); }
if(r>mid)
{ nim = min(nim,query2(rs(p),l,r)); }
return nim;
}
inline void swap(int &a,int &b)
{ int t=a; a=b; b=t; }
inline int QUERY(int a,int b)
{
int sum = 0,nim = 0x3f3f3f3f;
while(top[a]!=top[b])
{
if(dep[top[a]]<dep[top[b]])
{ swap(a,b); }
if(tot[top[a]][a]||tot[a][top[a]])
{ sum += tot[top[a]][a]; }
else
{ sum += tot[top[a]][a] = tot[a][top[a]] = query1(1,dfn[top[a]],dfn[a]); }
if(ni[top[a]][a]||ni[a][top[a]])
{ nim = min(nim,ni[top[a]][a]); }
else
{ nim = min(nim,ni[top[a]][a] = ni[a][top[a]] = query2(1,dfn[top[a]],dfn[a])); }
a = fa[top[a]];
}
if(dep[a]>dep[b])
{ swap(a,b); }
if(tot[a][b]||tot[b][a])
{ sum += tot[a][b]; }
else
{ sum += tot[a][b] = tot[b][a] = query1(1,dfn[a]+1,dfn[b]); }
if(ni[a][b]||ni[a][b])
{ nim = min(nim,ni[a][b]); }
else
{ nim = min(nim,ni[a][b] = ni[b][a] = query2(1,dfn[a],dfn[b])); }
return sum*nim;
}
}Tree;
inline int max(int a,int b)
{ return a>b?a:b; }
signed main()
{
//freopen("node.in","r",stdin);
t = read();
while(t--)
{
n = read();
for(re int i=1; i<=n; i++)
{ w[i][0] = read(); }
for(re int i=1; i<=n-1; i++)
{
int u = read(),v = read(),dis = read();
add(u,v,dis),add(v,u,dis);
}
cnt = 0;
dfs1(1,0,0),dfs2(1,1);
Tree.build(1,1,n);
for(re int i=1; i<=n-1; i++)
{
for(re int j=i+1; j<=n; j++)
{ ans = max(ans,Tree.QUERY(i,j)); }
}
printf("%lld\n",ans);
ans = 0,cnt = 1;
memset(ni,0,sizeof(ni));
memset(tot,0,sizeof(tot));
memset(Tree.st,0,sizeof(Tree.st));
for(re int i=1; i<=n; i++)
{
top[i] = size[i] = fa[i] = dfn[i] = 0;
edge[i] = edge[i+n] = (Graph){0,0,0};
id[i][0] = id[i][1] = head[i] = w[i][0] = w[i][1] = son[i] = 0;
}
}
return 0;
}
}
signed main()
{ return OMA::main(); }
正解很妙,我们首先将权值从大达到小排序,然后用并查集来维护一下该点所在集合的最长路及最长路的端点,为了叙述方便,我们设当前合并的两个集合中最长路的端点分别为 \(l_{1},r_{1},l_{2},r_{2}\) ,那么合并时,则会有六种情况,分别为
- \(l_{1},l_{2}\) 构成集合中的最长路
- \(r_{1},r_{2}\) 构成集合中的最长路
- \(l_{1},r_{2}\) 构成集合中的最长路
- \(r_{1},l_{2}\) 构成集合中的最长路
- \(l_{1},r_{1}\) 构成集合中的最长路
- \(l_{2},r_{2}\) 构成集合中的最长路
复制粘贴
分类讨论一下就好,两点之间的距离可以通过LCA来求,答案在合并的时候更新就好,注意,如果该点权值比当前点权值小,那么该点对当前点就没有贡献,不需要合并,手模一下就会很好理解。
类似的,这类有树上路径中权值最小/最大的点/边与路径做运算的题,可以考虑将点权/边权排序后用并查集来维护路径长,按顺序向集合中加点/边,这样后加的点/边权值一定是当前最大/最小的,能够对答案产生影响。
Code#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 100001
#define re register
#define int long long
namespace OMA
{
int t,n,ans;
struct Graph
{
int next;
int to;
int w;
}edge[MAX<<1];
struct DSU
{
int fa;
int l,r;
int dis;
}dsu[MAX];
struct POINTS
{
int val,u;
friend bool operator <(const POINTS &a,const POINTS &b)
{ return a.val>b.val; }
}p[MAX];
int val[MAX];
int cnt=1,head[MAX];
int w[MAX],bin[MAX];
int dep[MAX],fa[MAX][50];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline void add(int u,int v,int w)
{
edge[++cnt].next = head[u];
edge[cnt].to = v;
edge[cnt].w = w;
head[u] = cnt;
}
inline void dfs(int u,int fat)
{
dep[u] = dep[fa[u][0] = fat]+1;
for(re int i=1; i<=bin[dep[u]]; i++)
{ fa[u][i] = fa[fa[u][i-1]][i-1]; }
for(re int i=head[u]; i; i=edge[i].next)
{
int v = edge[i].to;
if(v!=fat)
{ w[v] = w[u]+edge[i].w,dfs(v,u); }
}
}
inline void swap(int &a,int &b)
{ int t=a; a=b; b=t; }
inline int LCA(int a,int b)
{
if(dep[a]<dep[b])
{ swap(a,b); }
while(dep[a]>dep[b])
{ a = fa[a][bin[dep[a]-dep[b]]]; }
if(a==b)
{ return a; }
for(re int i=bin[dep[a]]; ~i; i--)
{
if(fa[a][i]!=fa[b][i])
{ a = fa[a][i],b = fa[b][i]; }
}
return fa[a][0];
}
inline int max(int a,int b)
{ return a>b?a:b; }
inline int len(int a,int b)
{ return w[a]+w[b]-2*w[LCA(a,b)]; }
inline int find(int x)
{ return (dsu[x].fa!=x)?dsu[x].fa = find(dsu[x].fa):dsu[x].fa; }
inline void merge(int a,int b)
{
int r1 = find(a),r2 = find(b);
if(r1!=r2)
{
int ll = len(dsu[r1].l,dsu[r2].l);
int rr = len(dsu[r1].r,dsu[r2].r);
int lr = len(dsu[r1].l,dsu[r2].r);
int rl = len(dsu[r1].r,dsu[r2].l);
int dis = len(a,b),l = a,r = b;
if(ll>dis)
{ dis = ll,l = dsu[r1].l,r = dsu[r2].l; }
if(rr>dis)
{ dis = rr,l = dsu[r1].r,r = dsu[r2].r; }
if(lr>dis)
{ dis = lr,l = dsu[r1].l,r = dsu[r2].r; }
if(rl>dis)
{ dis = rl,l = dsu[r1].r,r = dsu[r2].l; }
if(dsu[r1].dis>dis)
{ dis = dsu[r1].dis,l = dsu[r1].l,r = dsu[r1].r; }
if(dsu[r2].dis>dis)
{ dis = dsu[r2].dis,l = dsu[r2].l,r = dsu[r2].r; }
dsu[r2].fa = r1;
dsu[r1].dis = dis,dsu[r1].l = l,dsu[r1].r = r;
ans = max(ans,val[a]*dis);
}
}
signed main()
{
t = read();
for(re int i=2; i<=MAX; i++)
{ bin[i] = bin[i>>1]+1; }
while(t--)
{
n = read();
for(re int i=1; i<=n; i++)
{ p[i] = (POINTS){val[i] = read(),i}; }
for(re int i=1; i<=n; i++)
{ dsu[i] =(DSU){i,i,i,0}; }
std::sort(p+1,p+1+n);
for(re int i=2; i<=n; i++)
{
int u = read(),v = read(),Dis = read();
add(u,v,Dis),add(v,u,Dis);
}
dfs(1,0);
for(re int i=1; i<=n; i++)
{
for(re int j=head[p[i].u]; j; j=edge[j].next)
{
if(val[p[i].u]<=val[edge[j].to])
{ merge(p[i].u,edge[j].to); }
}
}
printf("%lld\n",ans);
ans = 0,cnt = 1;
memset(fa,0,sizeof(fa));
for(re int i=1; i<=n; i++)
{ head[i] = dep[i] = 0; }
}
return 0;
}
}
signed main()
{ return OMA::main(); }
T3
没改出来,正解看不太懂。但应该可以类比hotel 做出来