题目

比赛界面

T1

数据范围明示直接\(O(n^2)\)计算,问题就在如何快速计算。

树上路径统计通常会用到差分方法。这里有两棵树,因此我们可以做“差分套差分”,在 A 树上对 B 的差分信息进行差分。在修改的时候,我们就会在 A 上 4 个位置进行修改,每次修改会涉及 B 上 4 个位置的差分修改,因此总共会涉及 16 个差分信息的修改。

回收标记的时候,我们可以先在 A 树上进行 DFS ,回收好子树内的差分信息后,再进行一次 B 的回收,得到当前节点上 B 的真实信息。

时间是\(O(n^2+q\log_2n)\)。

#include <cmath>
#include <cstdio> typedef long long LL; const int MAXN = 1e4 + 5, MAXLOG = 15; template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
} template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
} struct Tree
{
struct edge
{
int to, nxt;
}Graph[MAXN << 1]; int f[MAXN][MAXLOG];
int head[MAXN], dep[MAXN], seq[MAXN];
int n, ID, lg2, cnt; void addEdge( const int from, const int to )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
head[from] = cnt;
} void DFS( const int u, const int fa )
{
f[u][0] = fa, seq[++ ID] = u, dep[u] = dep[fa] + 1;
for( int i = head[u], v ; i ; i = Graph[i].nxt )
if( ( v = Graph[i].to ) ^ fa )
DFS( v, u );
} void init()
{
read( n );
for( int i = 1, a, b ; i < n ; i ++ )
read( a ), read( b ), addEdge( a, b ), addEdge( b, a );
DFS( 1, 0 );
lg2 = log2( n );
for( int j = 1 ; j <= lg2 ; j ++ )
for( int i = 1 ; i <= n ; i ++ )
f[i][j] = f[f[i][j - 1]][j - 1];
} void balance( int &u, const int stp ) const
{
for( int i = 0 ; ( 1 << i ) <= stp ; i ++ )
if( stp & ( 1 << i ) )
u = f[u][i];
} int LCA( int u, int v ) const
{
if( dep[u] > dep[v] ) balance( u, dep[u] - dep[v] );
if( dep[v] > dep[u] ) balance( v, dep[v] - dep[u] );
if( u == v ) return u;
for( int i = lg2 ; ~ i ; i -- ) if( f[u][i] ^ f[v][i] ) u = f[u][i], v = f[v][i];
return f[u][0];
} int fa( const int u ) const { return f[u][0]; }
}; Tree A, B; int dif[MAXN][MAXN];
LL ans; void change( int *d, const int u, const int v, const int lca, const int c )
{
d[u] += c, d[v] += c, d[lca] -= c, d[B.fa( lca )] -= c;
} void recovery( const int u, const int fa )
{
for( int i = A.head[u], v ; i ; i = A.Graph[i].nxt )
if( ( v = A.Graph[i].to ) ^ fa )
recovery( v, u );
for( int i = 1 ; i <= B.n ; i ++ ) dif[fa][i] += dif[u][i];
for( int i = B.n ; i ; i -- )
{
int cur = B.seq[i];
ans ^= 1ll * u * cur * dif[u][cur];
dif[u][B.fa( cur )] += dif[u][cur];
}
} int main()
{
int Q, a1, a2, b1, b2, c, lcaa, lcab;
A.init(), B.init(), read( Q );
while( Q -- )
{
read( a1 ), read( a2 ), read( b1 ), read( b2 ), read( c );
lcaa = A.LCA( a1, a2 ), lcab = B.LCA( b1, b2 );
change( dif[a1], b1, b2, lcab, c );
change( dif[a2], b1, b2, lcab, c );
change( dif[lcaa], b1, b2, lcab, -c );
change( dif[A.fa( lcaa )], b1, b2, lcab, -c );
}
recovery( 1, 0 );
write( ans ), putchar( '\n' );
return 0;
}

T2

神奇的 DP 配合优化,放个官方题解吧:

若每种段只出现一次,可以注意到我们每涂完一种颜色,就将整个序列分为两段,所以,我们不妨令\(f_{i,j}\)表示涂完第\(i\)种颜色到第\(j\)种颜色,最多可以涂多少。那么我们有\(dp_{l,r} = max_{l\leq k \leq r}dp_{l,k−1} + dp_{k+1,r} + cost_{i,j}\),注意到该式虽然与四边形不等式稍有区别,但通过一样的不等式分析仍然可以得到\(dp_{i,j} + dp_{i+1,j+1} \geq dp_{i,j+1} + dp_{i + 1, j}\),故上式可以直接用四边形不等式优化。 一个有意思的现象是,上述决策点似乎只会出现在\(l\)或\(r\),但我并没有想出有效的证明方法。


T3

字符串简单题,考虑容斥。总方案很好计算,然后考虑不相交字符串的方案数。枚举分界点,然后计算左边和右边的回文串数量即可。

所以我为什么会想到奇怪的 slink 方法呀......它还不卡我......

所以就贴了 slink 的代码。

#include <cstdio>
#include <cstring> typedef long long LL; const int MAXN = 2e6 + 5;
const int mod = 998244353, inv2 = 499122177; template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
} template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
} int BIT[MAXN], g[MAXN];
int ed[MAXN], slink[MAXN], dif[MAXN];
int ch[MAXN][26], len[MAXN], fa[MAXN], dep[MAXN];
int N, tot, lst;
char S[MAXN]; void add( int &x, const int v ) { x = ( x + v >= mod ? x + v - mod : x + v ); }
int sub( const int x, const int v ) { return ( x < v ? x - v + mod : x - v ); }
LL mul( const int x, const LL v ) { LL tmp = x * v; return tmp < mod ? tmp : tmp % mod; } void up( int &x ) { x += x & ( -x ); }
void down( int &x ) { x -= x & ( -x ); }
void update( int x, const int v ) { for( ; x <= N ; up( x ) ) add( BIT[x], v ); }
int getSum( int x ) { int ret = 0; for( ; x ; down( x ) ) add( ret, BIT[x] ); return ret; }
int query( const int l, const int r ) { return sub( getSum( r ), getSum( l - 1 ) ); } void build()
{
int x;
N = strlen( S + 1 );
len[fa[0] = ++ tot] = -1;
for( int i = 1 ; i <= N ; i ++ )
{
x = S[i] - 'a';
while( S[i] ^ S[i - len[lst] - 1] ) lst = fa[lst];
if( ! ch[lst][x] )
{
int cur = ++ tot, p = fa[lst];
while( S[i] ^ S[i - len[p] - 1] ) p = fa[p];
len[cur] = len[lst] + 2, fa[cur] = ch[p][x], ch[lst][x] = cur;
}
ed[i] = lst = ch[lst][x];
}
} int main()
{
int ans = 0;
scanf( "%s", S + 1 ), build();
for( int i = 2 ; i <= tot ; i ++ )
{
dep[i] = dep[fa[i]] + 1, dif[i] = len[i] - len[fa[i]];
if( dif[i] ^ dif[fa[i]] ) slink[i] = fa[i];
else slink[i] = slink[fa[i]];
}
for( int i = 1 ; i <= N ; i ++ )
{
for( int p = ed[i] ; p ; p = slink[p] )
{
g[p] = query( i - len[slink[p]] - dif[p] + 1, i );
if( slink[p] ^ fa[p] )
{
add( g[p], g[fa[p]] );
add( g[p], mul( ( dep[p] - dep[slink[p]] - 1 ), query( i - dif[p], i ) ) );
}
add( ans, g[p] );
}
add( ans, mul( mul( dep[ed[i]], dep[ed[i]] - 1 ), inv2 ) );
update( i, dep[ed[i]] );
}
write( ans ), putchar( '\n' );
return 0;
}

小结

对于简单题和基础的技巧都还不太熟练,要多加运用。

[noi.ac省选模拟赛]第12场题解集合的更多相关文章

  1. [noi.ac省选模拟赛]第10场题解集合

    题目 比赛界面. T1 不难想到,对于一个与\(k\)根棍子连接的轨道,我们可以将它拆分成\(k+1\)个点,表示这条轨道不同的\(k+1\)段. 那么,棍子就成为了点与点之间的边.可以发现,按照棍子 ...

  2. [noi.ac省选模拟赛]第11场题解集合

    题目   比赛界面. T1   比较简单.容易想到是求鱼竿的最大独立集.由于题目的鱼竿可以被分割为二分图,就可以想到最大匹配.   尝试建边之后会发现边的数量不小,但联系题目性质会发现对于一条鱼竿,它 ...

  3. NOI.AC省选模拟赛第一场 T1 (树上高斯消元)

    link 很容易对于每个点列出式子 \(f_{x,y}=(f_{x,y-1}+f_{x,y}+f_{x,y+1}+f_{x+1,y})/4\)(边角转移类似,略) 这个转移是相互依赖的就gg了 不过你 ...

  4. [NOI.AC省选模拟赛3.31] 星辰大海 [半平面交]

    题面 传送门 思路 懒得解释了......也是比较简单的结论 但是自己看到几何就退缩了...... 下周之内写一个计算几何的学习笔记! Code #include<iostream> #i ...

  5. [NOI.AC省选模拟赛3.31] 附耳而至 [平面图+最小割]

    题面 传送门 思路 其实就是很明显的平面图模型. 不咕咕咕的平面图学习笔记 用最左转线求出对偶图的点,以及原图中每个边两侧的点是谁 建立网络流图: 源点连接至每一个对偶图点,权值为这个区域的光明能量 ...

  6. [NOI.AC省选模拟赛3.30] Mas的童年 [二进制乱搞]

    题面 传送门 思路 这题其实蛮好想的......就是我考试的时候zz了,一直没有想到标记过的可以不再标记,总复杂度是$O(n)$ 首先我们求个前缀和,那么$ans_i=max(pre[j]+pre[i ...

  7. [NOI.AC省选模拟赛3.23] 染色 [点分治+BFS序]

    题面 传送门 重要思想 真的是没想到,我很久以来一直以为总会有应用的$BFS$序,最终居然是以这种方式出现在题目中 笔记:$BFS$序可以用来处理限制点对距离的题目(综合点分树使用) 思路 本题中首先 ...

  8. [NOI.AC省选模拟赛3.23] 集合 [数学]

    题面 传送门 一句话题意: 给定$n\leq 1e9,k\leq 1e7,T\leq 1e9$ 设全集$U=\lbrace 1,2,3,...n\rbrace $,求$(min_{x\in S}\lb ...

  9. [noi.ac省选模拟赛20200606]赌怪

    题目   点这里看题目. 分析   先特判掉\(K=2\)的情况.   首先可以考虑到一个简单 DP :   \(f(i)\):前\(i\)张牌的最大贡献.   转移可以\(O(n^2)\)地枚举区间 ...

随机推荐

  1. Java并发编程入门(二)

    1.竞态条件 1.1 定义 当某个计算的正确性取决于多个线程的交替执行时序时,就会发生竞态条件.换句话说,正确的结果要取决于运气. 最常见的竞态条件类型:先检查后执行(Check-Then-Act)操 ...

  2. C语言数据类型整理

    基本类型: 它们是算术类型,包括两种类型:整数类型和浮点类型. 枚举类型: 它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量. void 类型: 类型说明符 void 表明没有可用 ...

  3. MongoDB -MSC集群的部署

    一.Shard节点配置过程 1. 目录创建:mkdir -p /mongodb/38021/conf  /mongodb/38021/log  /mongodb/38021/datamkdir -p ...

  4. C# 数据操作系列 - 17 Dapper ——号称可以与ADO.NET 同台飙车的ORM

    0. 前言 之前四篇介绍了一个国内开发者开发的优秀框架SqlSugar,给我们眼前一亮的感觉.这一篇,我们将试试另一个出镜率比较高的ORM框架-Dapper. Dapper是一个轻量级的ORM框架,其 ...

  5. [Unity2d系列教程] 004.Unity如何调用ios的方法(SDK集成相关)

    和上一篇类似,我们同样希望Unity能够直接调用IOS底层的代码,那么我们就需要研究怎么去实现它.下面让我来带大家看一个简单的例子 1.创建.h和.m文件如下 .h // // myTest.m // ...

  6. [leetcode] 并查集(Ⅱ)

    最长连续序列 题目[128]:链接. 解题思路 节点本身的值作为节点的标号,两节点相邻,即允许合并(x, y)的条件为x == y+1 . 因为数组中可能会出现值为 -1 的节点,因此不能把 root ...

  7. 【译】Welcome to C# 9.0

    C# 9.0正在形成,我想分享我们对添加到该语言下个版本的一些主要功能的看法.对于每个新版本的 C#,我们努力使常见的编码方案更加清晰和简单,C# 9.0 也不例外.这次的一个特别重点是支持数据形状的 ...

  8. day08 for循环与字符串掌握操作

    # 1.什么是for循环# 循环就是重复做某件事情,for循环是python提供第二种循环机制# 2.为何:理论上for循环可以做的事情while循环也可以做# for循环再循环取值(遍历取值)比wh ...

  9. WebSocket是什么,有什么作用和特点?

    WebSocket是一种在单个TCP连接上进行全双工通信的协议. Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手.具有持久化的特性 特点: 保持连接状态.与HTTP ...

  10. Chisel3 - Tutorial - Stack

    https://mp.weixin.qq.com/s/-AVJD1IfvNIJhmZM40DemA   实现后入先出(last in, first out)的栈.   参考链接: https://gi ...