夜莺与玫瑰,影子,玫瑰花精

题目名好像都是童话故事

T1

首先,dead line 是一条直线,而不是线段。考试的时候一直以为是线段,那么横竖共有n+m条,考虑斜着的,斜着的交点为有穷的,则需要满足斜率不同,那么只需要统计一边的,再乘2就好了,显然gcd=1时统计答案,则有,

\[ans=\sum_{i-1}^{n-1}\sum_{j=1}^{m-1}[\gcd(i,j)=1](n-i)\times(m-j)-\max(n-2i,0)\times \max(m-2j,0) \]

然后有60pts的好成绩,如何优化,考虑维护二维前缀和,设 \(gcd_{i,j}\) 表示到i,j时,gcd为1的数量,设 \(sum_{i,j}\) 表示到i,j斜线的数量,则有,

\[sum_{i,j}=sum_{i-1,j}+sum_{i,j+1}-sum_{i-1,j-1}+gcd_{i,j}-gcd_{\frac{i}{2},\frac{j}{2}} \]

Code
#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 做出来