[noi.ac省选模拟赛]第10场题解集合
题目
比赛界面。
T1
不难想到,对于一个与\(k\)根棍子连接的轨道,我们可以将它拆分成\(k+1\)个点,表示这条轨道不同的\(k+1\)段。
那么,棍子就成为了点与点之间的边。可以发现,按照棍子连边之后,我们一定可以得到一些链。假设每条轨道的最后一段作为链头,查询实际上就是查询所在链的链头。
使用 LCT 或 Splay 维护这些链即可,时间\(O(n\log_2n)\)。
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 2e6 + 5;
const int INF = 0x3f3f3f3f;
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' );
}
template<typename _T>
void swapp( _T &x, _T &y )
{
_T t = x; x = y, y = t;
}
template<typename _T>
_T MAX( const _T a, const _T b )
{
return a > b ? a : b;
}
vector<int> bar[MAXN];
int ch[MAXN][2], fa[MAXN];
int id[MAXN], lef[MAXN], rig[MAXN];
int beg[MAXN], bel[MAXN];
int N, M, Q, nsiz;
bool chk( const int x ) { return ch[fa[x]][1] == x; }
bool isrt( const int x ) { return ch[fa[x]][0] ^ x && ch[fa[x]][1] ^ x; }
bool nrt( const int x ) { return ! isrt( x ); }
void rotate( const int x )
{
if( ! x || isrt( x ) ) return ;
int y = fa[x], z = fa[y], side = chk( x ), son = ch[x][! side];
if( z && nrt( y ) ) ch[z][chk( y )] = x; ch[x][! side] = y, ch[y][side] = son;
if( son ) fa[son] = y; fa[y] = x, fa[x] = z;
}
void splay( const int x )
{
for( int y ; nrt( x ) ; rotate( x ) )
if( nrt( y = fa[x] ) )
rotate( chk( y ) == chk( x ) ? y : x );
}
void access( int x ) { for( int y = 0 ; x ; x = fa[y = x] ) splay( x ), ch[x][1] = y; }
int query( int x )
{
access( x ), splay( x );
while( ch[x][0] ) x = ch[x][0];
splay( x ); return x;
}
int main()
{
int tmp;
read( N ), read( M );
for( int i = 1 ; i <= M ; i ++ ) read( id[i] ), bar[id[i]].push_back( i ), bar[id[i] + 1].push_back( i );
for( int i = 1 ; i <= N ; i ++ )
{
tmp = bar[i].size(), beg[i] = nsiz + 1;
for( int j = 0, ref ; j < tmp ; j ++ )
{
ref = bar[i][j], ++ nsiz;
bel[nsiz] = i;
if( id[ref] == i ) lef[ref] = nsiz;
else rig[ref] = nsiz;
}
bel[ ++ nsiz] = i;
}
for( int i = 1 ; i <= M ; i ++ ) fa[lef[i]] = rig[i] + 1, fa[rig[i]] = lef[i] + 1;
int opt, x, u, v;
read( Q );
while( Q -- )
{
read( opt ), read( x );
if( opt == 1 )
{
u = lef[x], v = rig[x];
access( u ), splay( u ), fa[ch[u][0]] = 0, ch[u][0] = 0;
access( v ), splay( v ), fa[ch[v][0]] = 0, ch[v][0] = 0;
access( u + 1 ), splay( u + 1 ), fa[u + 1] = u, ch[u][0] = u + 1;
access( v + 1 ), splay( v + 1 ), fa[v + 1] = v, ch[v][0] = v + 1;
}
if( opt == 2 ) write( bel[query( beg[x] )] ), putchar( '\n' );
}
return 0;
}
T2
题解给出了有关欧拉路的做法:
如果所有点的度数都是偶数,那么我们只需要找一条欧拉回路,然后间隔着删边就好。否则,我们可以通过连边使得奇点变偶点,然后求一条欧拉回路,这时图被若干条链覆盖,我们同样可以是间隔着对每条链进行删边操作。
然鹅我并不想鸟它
实际上,由于题目保证有解,那么......大概,只需要删除所有可删除的边就可以了。
#include <cmath>
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 1e6 + 5;
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' );
}
vector<int> G[MAXN];
int fr[MAXN], to[MAXN];
int d[MAXN], T[MAXN];
int N, M;
bool used[MAXN];
void addE( const int from, const int to )
{
G[from].push_back( to ), d[from] ++;
}
int main()
{
int p, u, v;
read( N ), read( M );
for( int i = 1 ; i <= M ; i ++ ) read( fr[i] ), read( to[i] ), addE( fr[i], i ), addE( to[i], i );
for( int i = 1 ; i <= N ; i ++ ) T[i] = ceil( 1.0 * d[i] / 2 );
for( int i = 1 ; i <= N ; i ++ )
for( int j = 0 ; j < G[i].size() ; j ++ )
if( ! used[p = G[i][j]] )
{
u = fr[p], v = to[p];
if( d[u] == T[u] || d[v] == T[v] ) continue;
used[p] = true, d[u] --, d[v] --;
}
for( int i = 1 ; i <= M ; i ++ ) write( ! used[i] ), putchar( i == M ? '\n' : ' ' );
return 0;
}
T3
奇怪的计算几何题目。
可以发现性质:
\]
如果可以看到\(\frac n 2\)条边,就说明\(P\)在这些边与它们的对边的覆盖范围之外。又由于这\(\frac n 2\)条边一定在凸包上是按端点连续的,不可能有一组对边,因此\(P\)就无法被覆盖。多于\(\frac n 2\)的可以通过抛弃对边缩减到\(\frac n 2\)条。
怎么计算\(P\)能看到多少条边呢?我们考虑,先找出一个它能看到的点,再进行向左向右的二分,就可以找出\(P\)能看到的端点的区间,进而找到边的区间。
怎么找到最初的点呢?很简单,我们随机一组对边,如果\(P\)可以被它们覆盖,我们就可以直接输出。否则, 4 个点中肯定有至少一个\(P\)可以看到,我们判断一下就可以了。
计算只涉及叉积,在使用 __int128 的情况下不存在精度问题。
好烦的题,谁写谁恶心
#include <cstdio>
#include <cstdlib>
typedef __int128 LL;
#define int LL
const LL e = 1;
const int MAXN = 1e5 + 5;
template<typename _T>
inline void read ( _T &x )
{
x = 0; int f = 1; char s = getchar ();
for ( ; s < '0' || '9' < s; s = getchar () ) f = s == '-' ? -f : f;
for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
x *= f;
}
struct point
{
LL x, y;
point() { x = y = 0; }
point( const LL X, const LL Y ) { x = X, y = Y; }
};
struct vector
{
LL x, y;
vector() { x = y = 0; }
vector( const LL X, const LL Y ) { x = X, y = Y; }
vector( point S, point T ) { x = T.x - S.x, y = T.y - S.y; }
LL operator * ( const vector &b ) const { return x * b.y - y * b.x; }
};
point P[MAXN];
int N, Q;
int go( const int c, const int step ) { return ( c + step - 1 + N ) % N + 1; }
bool visible( const point Q, const int i )
{
LL t1 = vector( P[i], Q ) * vector( Q, P[i % N + 1] );
LL t2 = vector( P[i], Q ) * vector( Q, P[( i - 2 + N ) % N + 1] );
return t1 > 0 || t2 < 0;
}
signed main()
{
srand( 998244353 );
LL A, B; point R;
int l, r, mid, lef, rig, sta;
int T, lst = 0;
read( T ), read( N );
for( int i = 1 ; i <= N ; i ++ ) read( P[i].x ), read( P[i].y );
read( Q );
int hal = N / 2, t1, t2;
while( Q -- )
{
read( A ), read( B );
if( T ) A ^= e * lst * lst * lst;
if( T ) B ^= e * lst * lst * lst;
R = point( A, B );
int p = rand() % N + 1, q = ( p + N / 2 - 1 ) % N + 1;
t1 = vector ( P[p], R ) * vector ( R, P[p % N + 1] );
t2 = vector ( P[q % N + 1], R ) * vector ( R, P[q] );
if ( ! t1 || ! t2 || ( t1 < 0 && t2 > 0 ) )
{
puts ( "DA" ), ++ lst;
continue;
}
sta = visible ( R, p ) ? p : q;
l = 0, r = N - 1;
while( l < r )
{
mid = l + r + 1 >> 1;
if( visible ( R, ( sta - mid - 1 + N ) % N + 1 ) ) l = mid;
else r = mid - 1;
}
rig = l;
l = 0, r = N - 1;
while( l < r )
{
mid = l + r + 1 >> 1;
if( visible ( R, ( sta + mid - 1 ) % N + 1 ) ) l = mid;
else r = mid - 1;
}
lef = l;
int len = lef + rig;
if( len >= hal ) puts( "NE" );
else lst ++, puts( "DA" );
}
return 0;
}
小结
计算几何知识为 0 弱爆了,需要加强。
虽然 T1 一眼想到方法,然而花了很长时间找合适的数据结构,说明自己还不熟悉。
[noi.ac省选模拟赛]第10场题解集合的更多相关文章
- [noi.ac省选模拟赛]第12场题解集合
题目 比赛界面. T1 数据范围明示直接\(O(n^2)\)计算,问题就在如何快速计算. 树上路径统计通常会用到差分方法.这里有两棵树,因此我们可以做"差分套差分",在 A 树上对 ...
- [noi.ac省选模拟赛]第11场题解集合
题目 比赛界面. T1 比较简单.容易想到是求鱼竿的最大独立集.由于题目的鱼竿可以被分割为二分图,就可以想到最大匹配. 尝试建边之后会发现边的数量不小,但联系题目性质会发现对于一条鱼竿,它 ...
- [noi.ac省选模拟赛20200606]赌怪
题目 点这里看题目. 分析 先特判掉\(K=2\)的情况. 首先可以考虑到一个简单 DP : \(f(i)\):前\(i\)张牌的最大贡献. 转移可以\(O(n^2)\)地枚举区间 ...
- NOI.AC省选模拟赛第一场 T1 (树上高斯消元)
link 很容易对于每个点列出式子 \(f_{x,y}=(f_{x,y-1}+f_{x,y}+f_{x,y+1}+f_{x+1,y})/4\)(边角转移类似,略) 这个转移是相互依赖的就gg了 不过你 ...
- [NOI.AC省选模拟赛3.31] 星辰大海 [半平面交]
题面 传送门 思路 懒得解释了......也是比较简单的结论 但是自己看到几何就退缩了...... 下周之内写一个计算几何的学习笔记! Code #include<iostream> #i ...
- [NOI.AC省选模拟赛3.31] 附耳而至 [平面图+最小割]
题面 传送门 思路 其实就是很明显的平面图模型. 不咕咕咕的平面图学习笔记 用最左转线求出对偶图的点,以及原图中每个边两侧的点是谁 建立网络流图: 源点连接至每一个对偶图点,权值为这个区域的光明能量 ...
- [NOI.AC省选模拟赛3.30] Mas的童年 [二进制乱搞]
题面 传送门 思路 这题其实蛮好想的......就是我考试的时候zz了,一直没有想到标记过的可以不再标记,总复杂度是$O(n)$ 首先我们求个前缀和,那么$ans_i=max(pre[j]+pre[i ...
- [NOI.AC省选模拟赛3.23] 染色 [点分治+BFS序]
题面 传送门 重要思想 真的是没想到,我很久以来一直以为总会有应用的$BFS$序,最终居然是以这种方式出现在题目中 笔记:$BFS$序可以用来处理限制点对距离的题目(综合点分树使用) 思路 本题中首先 ...
- [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 ...
随机推荐
- 苏浪浪 201771010120《面向对象程序设计(java)》第八周学习总结
1.实验目的与要求 (1) 掌握接口定义方法: (2) 掌握实现接口类的定义要求: (3) 掌握实现了接口类的使用要求: (4) 掌握程序回调设计模式: (5) 掌握Comparator接口用法: ( ...
- JVM_双亲委派机制
双亲委派机制及作用 什么是双亲委派机制 当`.class`文件需要被加载时,它首先把这个任务委托给他的上级类加载器,层层往上委托,如果上级的类加载器没有加载过,自己才会去加载这个类. 源码分析 pro ...
- F. Dominant Indices
题意:求每个点的子树中哪一层节点数最多,如果有节点数最多不唯一,取层数最小的. 题解:dus on tree 基本想法是对每一个节点都构建一个deep数组,然后从底向上更新过来,但是这样空间复杂度和时 ...
- bzoj1497最大闭权图基础题
1497: [NOI2006]最大获利 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5485 Solved: 2661[Submit][Status] ...
- Java——多线程基础知识
多线程进程和线程的区别:每一个进程拥有自己的一整套变量,而线程则共享数据.java.lang.Thread static void sleep(long millis) 线程休眠给定的毫秒数,用 ...
- linux静态网络设置
一:NET模式 第一种: 第二种方式: 三:重启服务
- 浅谈spring依赖注入
了解依赖注入 前言 先了解下控制反转--转自知乎的国哥 如果一个类A 的功能实现需要借助于类B,那么就称类B是类A的依赖,如果在类A的内部去实例化类B,那么两者之间会出现较高的耦合,一旦类B出现了问题 ...
- [ES6系列-07]Generator Function: 生成器函数
[原创]码路工人 Coder-Power 大家好,这里是码路工人有力量,我是码路工人,你们是力量. github-pages 博客园cnblogs Generator function 生成器函数是E ...
- 【Ubuntu】Ubuntu系统启动过程中,输入用户名与密码后登录一直卡在紫色界面问题(未解决,最后通过重装系统)
0. 前言 由于本电脑为公用电脑,可能由于其他人点了图像界面中推荐的内核更新,导致原来安装的NVIDIA显卡驱动 430 与升级后的 5.0 内核不兼容,从而导致输入用户名后登录一直卡在紫色界面.在排 ...
- Java集合(十)实现Map接口的HashMap
Java集合(十)继承Map接口的HashMap 一.HashMap简介(基于JDK1.8) HashMap是基于哈希表(散列表),实现Map接口的双列集合,数据结构是“链表散列”,也就是数组+链表 ...