童话故事专场

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. WPF技巧:通过代码片段管理器编写自己常用的代码模板提示效率

    在写自定义控件的时候,有一部分功能是当内部的值发生变化时,需要通知控件的使用者,而当我在写依赖项属性的时候,我可以通过popdp对应的代码模板来完成对应的代码,但是当我来写属性更改回调的时候,却发现没 ...

  2. ARTS第三周

    第三周.上周欠下了 赶紧补上,糟糕了 还有第四篇也得加紧了 难受. 1.Algorithm:每周至少做一个 leetcode 的算法题2.Review:阅读并点评至少一篇英文技术文章3.Tip:学习至 ...

  3. Django基础011-form&modelform

    1.form from django import forms from django.core.exceptions import ValidationError #出现异常时用的 from use ...

  4. python使用笔记004-冒泡排序

    冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法. 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小.首字母从Z到A)错误就把他们交换过来.走访元素 ...

  5. IP地址与子网化分

    IP地址与子网掩码 一.IP地址的组成 二.IP地址的分类                            1)A.B.C三类地址的组成                            2 ...

  6. SECURECRT 连接锐捷交换机CONSOLE

    协议选择Serial,端口选择COM1.波特率设置为9600.RTS/CTS要把勾去掉(关闭流控功能)

  7. win10禁止粘滞键 禁止按5次shift开启粘滞键

    如果你感觉粘滞键的快捷键影响了你的使用或想强行更改连续按5次上档键的指向的话,建议用你需要的程序替换%windir%\system32文件夹下面的sethc.exe @echo offclsdel / ...

  8. Red Hat系统下安装gcc

    这篇是在客户服务器上安装redis碰到的问题.服务器是RedHat,无法直接安装gcc,导致Redis无法安装的解决办法:    1.make redis时候报下面这样的错,原因就是gcc没有安装. ...

  9. python 爬取网络小说 清洗 并下载至txt文件

    什么是爬虫 网络爬虫,也叫网络蜘蛛(spider),是一种用来自动浏览万维网的网络机器人.其目的一般为编纂网络索引. 网络搜索引擎等站点通过爬虫软件更新自身的网站内容或其对其他网站的索引.网络爬虫可以 ...

  10. Spring解决Attribute tx bound to namespace httpwww.w3.org2000xmlns was already specified

    Spring|解决Attribute "tx" bound to namespace "http://www.w3.org/2000/xmlns/" was a ...