NOI 2016 Day1 题解
今天写了NOI2016Day1的题,来写一发题解。
T2 网格
Description
\(T\) 次询问,每次给出一个 \(n\times m\) 的传送门,上面有 \(c\) 个位置是蛐蛐,其余位置都是跳蚤,问至少要把多少个跳蚤换成蛐蛐才能使存在两只跳蚤不连通。
\(n,m\le 10^9,\sum c\le 10^5\)
Solution
可以想到的是答案一定是 \(-1,0,1,2\) 中的一个。
考虑如何判断。\(0\) 的话一定是存在两个及以上的跳蚤连通块,\(1\) 的话是只存在一个跳蚤连通块且存在一个割点,\(-1\) 就不用说了。\(2\) 就是除了这些情况的其他情况。
考虑如何快速判断。首先我们可以发现有用的其实只有在蛐蛐周围 \(5\times 5\) 的这些格子。
然后我们可以发现如果存在两个及以上的连通块,那么一定存在一个蛐蛐连通块使得周围有属于不同的跳蚤联通块的跳蚤。
于是我们就可以做到 \(\Theta(\sum c)\) 了。注意因为常数较大所以需要使用 \(\text{hash}\) 表。
Code
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define Int register int
#define mp make_pair
#define MAXN 2500005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int kase,n,m,c;
struct Hash{//实现hash表
private:
#define mod 1000007
#define int long long
int siz,sx[MAXN],sy[MAXN],nxt[MAXN],ind[MAXN],head[mod + 5];
public:
void clear(){siz = 0,memset (head,0,sizeof (head));}
void ins (int x,int y,int id){
int pos = (1ll * (x - 1) * m + y) % mod;
sx[++ siz] = x,sy[siz] = y,ind[siz] = id,nxt[siz] = head[pos],head[pos] = siz;
}
int query (int x,int y){
int pos = (1ll * (x - 1) * m + y) % mod;
for (Int i = head[pos];i;i = nxt[i]) if (sx[i] == x && sy[i] == y) return ind[i];
return 0;
}
#undef int
}h,bel,vis;
struct edge{
int v,nxt;
}e[MAXN * 4];
int toop = 1,head[MAXN];
void link (int u,int v){
e[++ toop] = edge {v,head[u]},head[u] = toop;
e[++ toop] = edge {u,head[v]},head[v] = toop;
}
bool cut[MAXN];
int ind,dfn[MAXN],low[MAXN];
void Tarjan (int u,int fa){
dfn[u] = low[u] = ++ ind;int son = 0;
for (Int i = head[u];i;i = e[i].nxt){
int v = e[i].v;
if (!dfn[v]) son ++,Tarjan (v,u),low[u] = min (low[u],low[v]),cut[u] |= (low[v] >= dfn[u]);
else low[u] = min (low[u],dfn[v]);
}
if (!fa && son == 1) cut[u] = 0;
}
pii t[MAXN];
int dx[8] = {1,-1,0,0,1,1,-1,-1},dy[8] = {0,0,1,-1,1,-1,1,-1};
bool inside (int x,int y){return x >= 1 && x <= n && y >= 1 && y <= m;}
bool checkit (){//判断唯二的两个点是否联通
for (Int x = 1;x <= n;++ x)
for (Int y = 1;y <= m;++ y) if (!h.query (x,y)){
for (Int i = 0;i < 4;++ i){
int tx = x + dx[i],ty = y + dy[i];
if (inside (tx,ty) && !h.query (tx,ty)) return 0;
}
}
return 1;
}
bool ner[MAXN];
int Abs (int x){return x < 0 ? -x : x;}
queue <pii> Q,q;
void BFS (int sx,int sy,int col){
Q.push (mp (sx,sy)),bel.ins (sx,sy,col);
while (!Q.empty()){
int x = Q.front().first,y = Q.front().second;Q.pop ();
for (Int i = 0;i < 4;++ i){
int tx = x + dx[i],ty = y + dy[i];
if (inside (tx,ty) && h.query (tx,ty) > 0 && !bel.query (tx,ty))
bel.ins (tx,ty,col),Q.push (mp (tx,ty));
}
}
}
int tot;
pii pnt[MAXN];
bool BFS1 (int sx,int sy){
tot = 0;Q.push (mp (sx,sy)),vis.ins (sx,sy,-1);
while (!Q.empty()){
int x = Q.front().first,y = Q.front().second;Q.pop ();
for (Int i = 0;i < 8;++ i){
int tx = x + dx[i],ty = y + dy[i];
if (inside (tx,ty) && h.query (tx,ty) == -1 && !vis.query (tx,ty))
vis.ins (tx,ty,-1),Q.push (mp (tx,ty));
else if (inside (tx,ty) && h.query (tx,ty) > 0 && !vis.query (tx,ty)) pnt[++ tot] = mp (tx,ty);
}
}
if (!tot) return 0;
for (Int i = 2,tmp = bel.query (pnt[1].first,pnt[1].second);i <= tot;++ i)
if (bel.query (pnt[i].first,pnt[i].second) != tmp) return 1;
return 0;
}
bool fuckit(){
int fuc = 0;bel.clear ();
while (!q.empty()){
int x = q.front().first,y = q.front().second;q.pop ();
if (bel.query (x,y)) continue;
BFS (x,y,++ fuc);
}
vis.clear ();
for (Int i = 1;i <= c;++ i)
if (!vis.query (t[i].first,t[i].second))
if (BFS1 (t[i].first,t[i].second)) return 1;
return 0;
}
signed main(){
read (kase);
while (kase --> 0){
while (!q.empty()) q.pop();
read (n,m,c);int cnt = 0;h.clear ();
for (Int i = 1;i <= c;++ i) read (t[i].first,t[i].second),h.ins (t[i].first,t[i].second,-1);
if (c >= 1ll * n * m - 1){puts ("-1");continue;}
else if (c == 1ll * n * m - 2){puts (checkit () ? "0" : "-1");continue;}
toop = 1,memset (head,0,sizeof (head));
for (Int z = 1;z <= c;++ z){
int x = t[z].first,y = t[z].second;
for (Int tx = max (1,x - 2);tx <= min (n,x + 2);++ tx)
for (Int ty = max (1,y - 2);ty <= min (m,y + 2);++ ty){
if (!h.query (tx,ty)){
h.ins (tx,ty,++ cnt),ner[cnt] = max (Abs (tx - x),Abs (ty - y)) <= 1,q.push (mp (tx,ty));
for (Int i = 0;i < 4;++ i){
int x1 = tx + dx[i],y1 = ty + dy[i];
if (inside (x1,y1) && h.query (x1,y1) > 0) link (cnt,h.query (x1,y1));
}
}
else if (h.query (tx,ty) > 0 && max (Abs (tx - x),Abs (ty - y)) <= 1) ner[h.query (tx,ty)] = 1;
}
}
if (fuckit ()){puts ("0");continue;}
if (n == 1 || m == 1){puts ("1");continue;}
ind = 0;for (Int i = 1;i <= cnt;++ i) cut[i] = dfn[i] = 0;
for (Int i = 1;i <= cnt;++ i) if (!dfn[i]) Tarjan (i,0);
for (Int i = 1;i <= cnt;++ i) if (cut[i] && ner[i]){puts ("1");goto there;}
puts ("2");
there:;
}
return 0;
}
T3 循环之美
Description
给出 \(n,m,k\),求出 \(a,b\) 满足 \(1\le a\le n,1\le b\le m\) 且 \(a/b\) 在 \(k\) 进制下小数部分纯循环的本质不同个数。
\(n,m\le 10^9,k\le 2000\)
Solution
首先可以观察到的是,当 \(\gcd(a,b)=1\) 且 \(\gcd(b,k)=1\)的时候会产生贡献。
然后就是莫反推式子了:
\]
\]
\]
\]
然后你发现后面那一坨可以直接 \(\Theta(k\log k)\) 预处理,然后考虑前面怎么弄。
设 \(g(N,K)=\sum_{d=1}^{N} \mu(d)[\gcd(d,K)=1]\)
那么可以得到 \(g(N,K)=\sum_{d=1}^{N} \mu(d)\sum_{h|d\wedge h|k} \mu(h)\)
\]
\]
边界条件就是 \(g(N,0)=0,g(N,1)=\sum_{i=1}^{N} \mu(i)\),这个可以直接杜教筛。
总的复杂度我不是很会算,据说是 \(\Theta(k\log k+n^{2/3}+d(k)\times \sqrt n)\) 的,其中 \(d(k)\) 表示 \(k\) 的质因子个数。
Code
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 1000005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int n,m,k;
int gcd (int a,int b){return !b ? a : gcd (b,a % b);}
bool vis[MAXN];
int tot,mu[MAXN],pre[MAXN],prime[MAXN];
void Euler (int up){
mu[1] = pre[1] = 1;
for (Int i = 2;i <= up;++ i){
if (!vis[i]) prime[++ tot] = i,mu[i] = -1;
for (Int j = 1;j <= tot && i * prime[j] <= up;++ j){
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
else mu[i * prime[j]] = -mu[i];
}
pre[i] = pre[i - 1] + mu[i];
}
}
unordered_map <int,int> mp;
int Getmu (int N){
if (N <= 1e6) return pre[N];
if (mp.find(N) != mp.end()) return mp[N];
int res = N >= 1;
for (Int l = 2,r;l <= N;l = r + 1)
r = N / (N / l),res -= (r - l + 1) * Getmu (N / l);
return mp[N] = res;
}
#define MAXM 2005
vector <int> vic[MAXM];
unordered_map <int,int> g[MAXM];
int getit (int N,int K){
if (K == 0 || N <= 0) return 0;
if (g[K].find (N) != g[K].end()) return g[K][N];
if (K == 1) return g[K][N] = Getmu (N);
int res = 0;
for (Int h : vic[K]) res += mu[h] * mu[h] * getit (N / h,h);
return g[K][N] = res;
}
int f[MAXM];
int getf (int N){
return (N / k) * f[k] + f[N % k];
}
signed main(){
read (n,m,k),Euler (1e6);
for (Int d = 1;d <= k;++ d) f[d] = f[d - 1] + (gcd (d,k) == 1);
for (Int d = 1;d <= k;++ d) if (mu[d]) for (Int K = d;K <= k;K += d) vic[K].push_back (d);
int tot = 0;
for (Int l = 1,r,las = 0,tmp;l <= min (n,m);l = r + 1)
r = min (n / (n / l),m / (m / l)),tmp = getit (r,k),
tot += (tmp - las) * getf (m / l) * (n / l),las = tmp;
write (tot),putchar ('\n');
return 0;
}
NOI 2016 Day1 题解的更多相关文章
- Noip 2016 Day1 题解
老师让我们刷历年真题, 然后漫不经心的说了一句:“你们就先做做noip2016 day1 吧” ...... 我还能说什么,,,,,老师你这是明摆着伤害我们啊2333333333 预计分数:100+2 ...
- NOI 2017 Day1 题解
被虐爆了... T1 整数 题目传送门 Description 有一个整数 \(x\),有 \(n\) 此操作,每次操作为以下两种情况: 给出 \(a,b\),将 \(x\) 加上 \(a\times ...
- [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分
[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...
- THUSC2017 Day1题解
THUSC2017 Day1题解 巧克力 题目描述 "人生就像一盒巧克力,你永远不知道吃到的下一块是什么味道." 明明收到了一大块巧克力,里面有若干小块,排成n行m列.每一小块都有 ...
- https://docs.huihoo.com/infoq/qconbeijing/2016/day1/工程效率提升专题/2-2-支持百度万人开发的工具装备及百度工程能力建设-夏仲璞.pdf
https://docs.huihoo.com/infoq/qconbeijing/2016/day1/工程效率提升专题/2-2-支持百度万人开发的工具装备及百度工程能力建设-夏仲璞.pdf http ...
- NOI 2016 优秀的拆分 (后缀数组+差分)
题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...
- Noi 2016
考砸只能说自己弱 Noi不是生活的全部, 人们也不会永远止步于失败. 大家加油 可以+我的qq:582744883
- 字符串(后缀自动机):NOI 2016 优秀的拆分
[问题描述] 如果一个字符串可以被拆分为 AABB 的形式,其中 A 和 B 是任意非空字符串, 则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A = aab, B ...
- 数据结构(线段树):NOI 2016 区间
[问题描述] [输入格式] [输出格式] [样例输入] 6 3 3 5 1 2 3 4 2 2 1 5 1 4 [样例输出] 2 [样例说明] [更多样例] 下载 [样例 2 输入输出] 见目录下的 ...
随机推荐
- 如何打一个RPM包
如何打一个RPM包 参考链接:RPM打包原理.示例.详解及备查 前言 本文只是一个RPM安装的例子,并没有对RPM做比较详尽的叙述,更为详尽的讲解,可以在上面的链接中找到. RPM是啥? RPM(Re ...
- UVA1620 Lazy Susan(结论证明)
结论: 当 \(n\geq 6\) 时,若 \(n\) 是奇数且输入序列的逆序对数是奇数,则无解,否则有解. 当 \(n=4\) 或 \(n=5\) 时,答案个数及其有限,只有这个环是 \(1\) 到 ...
- 并发控制--context篇
目录 1. 前言 2 Context 实现原理 2.1 接口定义 2.1 cancelCtx 2.1.1 Done()接口实现 2.1.2 Err()接口实现 2.1.3 cancel()接口实现 2 ...
- mysql 8.0 1405的坑
因版本差异:password 可能对应:authentication_string字段 不行的话再尝试一下,并且修改下密码类型 ALTER USER 'root'@'%' IDENTIFIED WI ...
- Ubuntu 16.04 Install NVidia Driver (download from nvidia official site)
sudo apt-get update sudo apt-mark hold libreoffice sudo apt-get update && sudo apt-get upgra ...
- WebService学习总结(六)--CXF 与Spring结合+tomcat发布
该项目在上文 WebService学习总结(四)--基于CXF的服务端开发 的基础上修改为spring上发布的webservice接口 1.新建web project 工程 2.导入spring ...
- BFD协议简介
1. 背景 双向转发检测BFD(Bidirectional Forwarding Detection)是一种全网统一的检测机制,用于快速检测.监控网络中链路或者IP路由的转发连通状况. 为了保护关键应 ...
- 页面调用系统window打印
一. 打印:直接页面调用window.print(),当前页面就会转换成打印页面 当前页面是使用HTML拼接成A4纸表格样式的展示: doPrint:function(type) { // this. ...
- JDBC使用案例
一.结果集中获取数据并对实体set值,封装成对象返回: 2.封装JDBC工具类 只要执行一次的,如读取配置文件,则写静态代码块: 异常抛出要写明方法才可以throw,静态代码块异常只能捕捉try ca ...
- JAVA安全基础之代理模式(一)
JAVA安全基础之代理模式(一) 代理模式是java的一种很常用的设计模式,理解代理模式,在我们进行java代码审计时候是非常有帮助的. 静态代理 代理,或者称为 Proxy ,简单理解就是事情我不用 ...