童话故事专场

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. [Vue入门及介绍,基础使用、MVVM架构、插值表达式、文本指令、事件指令]

    [Vue入门及介绍,基础使用.MVVM架构.插值表达式.文本指令.事件指令] 1)定义:javascript渐进式框架 ​ 渐进式:可以控制一个页面的一个标签,也可以控制一系列标签,也可以控制整个页面 ...

  2. 痞子衡嵌入式:串行NOR Flash的页编程模式对于量产时间的影响

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是串行NOR Flash的页编程模式对于量产时间的影响. 任何嵌入式产品最终都绕不开量产效率话题,尤其是对于主控是非内置 Flash 型 ...

  3. python使用笔记16--操作redis

    操作redis应先引入第三方模块 执行以下命令 pip install redis 1.redis常用方法 1 import redis 2 #decode_responses=True将bytes转 ...

  4. Gitbook配置目录折叠

    如果有多个目录,Gitbook在浏览器上打开时,默认所有的目录都会打开,当目录比较多时,全部显示不利于阅读. 可以使用插件配置目录折叠,使得打开浏览器时这些目录默认是关闭的. 在执行gitbook i ...

  5. CTF-Streamgame1-writeup

    Streamgame1 题目信息: 附件: streamgame1.py from flag import flag assert flag.startswith("flag{") ...

  6. 给App加上音频编辑功能,让你的用户Show起来

    如今短视频当道,BGM无处不在,用户在每个能秀的地方都想要加上个性表达的音频.作为一个开发者,需不断探索和迎合用户的行为喜好,音频编辑功能成为用户在编辑个人信息.内容创作.生活分享等场景下的必需品. ...

  7. Spring Boot邮箱链接注册验证

    Spring Boot邮箱链接注册验证 简单介绍 注册流程 [1]前端提交注册信息 [2]后端接受数据 [3]后端生成一个UUID做为token,将token作为redis的key值,用户数据作为re ...

  8. js检测客户端是否安装

    前言 需求背景:一个web下载页面,需要检测pc是否安装了客户端软件(windows软件).网页上有一个打开客户端按钮.若安装了客户端软件,则直接打开,否则下载软件.支持web下载页面在iframe下 ...

  9. 谷粒学院-2-mybatisplus

    一.参考文档 官网:http://mp.baomidou.com/ 参考教程:http://mp.baomidou.com/guide/ MyBatis-Plus(简称 MP)是一个 MyBatis ...

  10. 【动态规划】合唱队形 luogu-

    分析 做两遍最长上升子序列,在遍历一下,取最大值. AC代码 #include <bits/stdc++.h> using namespace std; #define ms(a,b) m ...