突然发现计蒜客noip的题目不错,大都是带一点算法又不太难的内种(acm铜牌线或以下),可以扫一下芝士盲区。
T1 小X的质数
求[L,R]区间内是质数或2质数积的数的个数 r<=1e7 最多1e5组询问
很基础的题 筛法搞一搞就好 O(nloglogn)的筛法筛2次再处理前缀和
注意这个复杂度下要用bool数组不然会超时 线性欧拉筛应该不会卡常数
T2 小X的密室
字面意思 二进制状态压缩现在状态下有的钥匙和每个房间的钥匙 位运算判断钥匙是否满足及到达房间获得钥匙后的状态
然后bfs搜索就好 链表存边 状态数n*2^k(也大概是时间复杂度) 但k大的时候有很多状态不可能存在 常数很小 不用剪枝优化什么的 没有什么坑点
T3 小X的佛光
树(无向无环图)中给三点abc 求a到b路径和c到b路径中重合的点数(就是重合的边数+1) 点数和询问数都是2*10^5
显然标记走过的点判重是会超时的 想到树的最近公共祖先 分析一下 发现只要知道abc三点两两的最邻近祖先 根据各点及对应祖先在树上的深度 就可以O(1)的算出重合边数
Tarjan算法可以离线的O(n+q)的一次处理所有询问(链表存边和询问) 模板套一下 复杂度是2*10^5+3*2*10^5 可以接受
或用倍增方法求最近公共祖先 总复杂度O((n+q)logn) 但常数小 也可以过
对于每组询问要处理abc两两的最近公共祖先 数组开大点就好
代码比较麻烦但想法不难
T1:
#include<cstdio>
bool p[10000009], p1[10000009];
int a[10000009], i, j;
int main()
{
for (i = 2; i < 10000009; i++)
if (p[i] == 0)
for (j = 2 * i; j < 10000009; j += i)
p[j] = 1;
for (i = 2; i < 10000009; i++)
if (p1[i] == 0)
for (j = 2 * i; j < 10000009; j += i)
if (p[j / i]) p1[j] = 1;
for (i = 2; i < 10000009; i++)
a[i] = a[i - 1] + (p1[i] == 0);
scanf("%*d");
while (~scanf("%d%d", &i, &j))
printf("%d\n", a[j] - a[i - 1]);
}
T2:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int n, m, nn[5005], kk = 1, h[5005], vi[5005][1111];
struct { int v, ne, z; }a[6006];
struct node { int sta, x; };
void add(int u, int v, int z)
{
a[kk].v = v, a[kk].z = z, a[kk].ne = h[u], h[u] = kk++;
}
int bfs()
{
int i, st, xx, ans = 1 << 28;
queue<node>q;
memset(vi, -1, sizeof(vi));
node ss, qq;
ss.x = 1, ss.sta = nn[1], vi[1][nn[1]] = 0;
q.push(ss);
while (!q.empty())
{
ss = q.front(), q.pop();
for (i = h[ss.x]; i; i = a[i].ne)
if ((ss.sta&a[i].z) == a[i].z)
{
xx = a[i].v, st = ss.sta | nn[xx];
if (vi[xx][st] == -1)
{
vi[xx][st] = vi[ss.x][ss.sta] + 1;
qq.x = xx, qq.sta = st, q.push(qq);
}
}
}
for (i = 0; i < 1025; i++)
if (vi[n][i] != -1)
ans = min(ans, vi[n][i]);
return ans;
}
int main()
{
int k, i, j, x, y, z, q, ans;
scanf("%d%d%d", &n, &m, &k);
for (i = 1; i <= n; i++)
for (j = 0; j < k; j++)
scanf("%d", &x), nn[i] <<= 1, nn[i] += x;
for (i = 0; i < m; i++)
{
scanf("%d%d", &x, &y), z = 0;
for (j = 0; j < k; j++)
scanf("%d", &q), z <<= 1, z += q;
add(x, y, z);
}
ans = bfs();
if (ans == 1 << 28) puts("No Solution");
else printf("%d\n", ans);
}
T3:
#include <cstdio>
#include <cstring>
#include<algorithm>
using namespace std;
#define mm 200005
struct { int u, v, w, lca, ne; }edge[mm << 2], edge1[mm << 3];
int head[mm], ip, head1[mm], ip1, m, n, fa[mm], vis[mm], ance[mm], dir[mm];
void add(int u, int v, int w)
{
edge[ip].v = v, edge[ip].w = w, edge[ip].ne = head[u], head[u] = ip++;
}
void add1(int u, int v)
{
edge1[ip1].u = u, edge1[ip1].v = v, edge1[ip1].lca = -1, edge1[ip1].ne = head1[u], head1[u] = ip1++;
}
int find(int x)
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
void Union(int x, int y)
{
x = find(x), y = find(y);
if (x != y)
fa[y] = x;
}
void tarjan(int u)
{
int i, v, w;
vis[u] = 1, ance[u] = fa[u] = u;
for (i = head[u]; i != -1; i = edge[i].ne)
{
v = edge[i].v, w = edge[i].w;
if (!vis[v])
dir[v] = dir[u] + w, tarjan(v), Union(u, v);
}
for (i = head1[u]; i != -1; i = edge1[i].ne)
{
v = edge1[i].v;
if (vis[v])
edge1[i].lca = edge1[i ^ 1].lca = ance[find(v)];
}
}
int main()
{
int u, v, w, i, lca, lcb, ans, lcc;
scanf("%d%d%*d", &n, &m);
memset(head, -1, sizeof(head)), memset(head1, -1, sizeof(head1));
for (i = 1; i < n; i++)
scanf("%d%d", &u, &v), add(u, v, 1), add(v, u, 1);
for (i = 0; i < m; i++)
{
scanf("%d%d%d", &u, &v, &w), add1(u, v), add1(v, u);
add1(w, v), add1(v, w), add1(u, w), add1(w, u);
}
dir[1] = 0, tarjan(1);
for (i = 0; i < m; i++)
{
u = edge1[i * 6].u, v = edge1[i * 6].v, lca = edge1[i * 6].lca;
lcb = edge1[i * 6 + 2].lca, w = edge1[i * 6 + 2].u;
lcc = edge1[i * 6 + 4].lca;
ans = dir[v] - max(dir[lca], dir[lcb]) + 1;
if (lca == lcb)
ans = dir[lcc] - dir[lca] + dir[v] - dir[lca] + 1;
printf("%d\n", ans);
}
}