童话故事专场

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

noip15的更多相关文章

  1. [Luogu 2678] noip15 子串

    [Luogu 2678] noip15 子串 题目描述 有两个仅包含小写英文字母的字符串 A 和 B.现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出 ...

  2. 20210714 noip15

    考前 mtr 中午拿着笔记本改题(Orz),一点多发现 13.50 有比赛(截止 12 点都没放出来),赶紧睡.13.40 到了学校,巨瞌睡,洗了把脸到机房发现推迟到 14.30 了,wcnm 趴在桌 ...

随机推荐

  1. bash的RANDOM变量生成的是真正的随机数吗

    static void seedrand () { struct timeval tv; gettimeofday (&tv, NULL); sbrand (tv.tv_sec ^ tv.tv ...

  2. Arduino IDE 2.0 beta安装

    1.在官网(Software | Arduino)下载安装包,此次提供操作系统有:Windows.Linux和macOC系统 2.点击安装包进行安装 3.点击我同意 4.点击下一步 5.选择安装路径( ...

  3. Python单元测试框架unittest之单用例管理(二)

    概述 利用python进行测试时,测试用例的加载方式有2种: 一种是通过unittest.main()来启动所需测试的测试模块,上篇文章就是使用的这种方式: 一种是添加到testsuite集合中再加载 ...

  4. C语言:位域详解

    有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可.例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二进位.正是基于这种考虑,C语言又提供了一种叫做位域 ...

  5. [刘阳Java]_什么是EasyUI_第1讲

    jQuery EasyUI在Java后台开发中用得还是比较多.当然客观来讲虽然前端技术的发展,很多后台界面设计都植入了前端技术的框架.但是这篇文章我个人觉得也不会妨碍我们对jQuery EasyUI的 ...

  6. Scala学习——函数高级操作

    scala函数高级操作 一.字符串高级操作 多行字符串和插值 package top.ruandb.scala.Course06 object StringApp { def main(args: A ...

  7. Leetcode:230. 二叉搜索树中第K小的元素

    Leetcode:230. 二叉搜索树中第K小的元素 Leetcode:230. 二叉搜索树中第K小的元素 思路: 利用BST的中序历遍的结果为其排序后的结果,我们可以利用其特性直接找到第k个中序遍历 ...

  8. 锁屏面试题百日百刷-java大厂八股文(day3)

    为了有针对性的准备面试,锁屏面试题百日百刷开始每日从各处收集的面经中选择几道经典面试题分享并给出答案供参考,答案中会做与题目相关的扩展,并且可能会抛出一定问题供思考.这些题目我会标注具体的公司.招聘类 ...

  9. python中进程详解

    1:pdb调试:基于命令行的调试工具,非常类似gnu和gdb调试,以下是常用的调试命令: 可以python -m pdb xxx.py(你的py文件名)进入命令行调试模式 命令 简写命令 作用 bea ...

  10. Spring最简单构建一个后台{msg:"登录成功",code:200,data:null}

    一.简介 {msg:"登录成功",code:200,data:null} 二.两种请求 如果严格msg code data也带"" @RestControlle ...