@ljt12138
2017-07-02T19:52:22.000000Z
字数 29642
阅读 806
之前一道题的增强版...
考虑所有都相等,则可以从深度由深到浅贪心,如果还没有被覆盖就从这个节点覆盖上去。
但对于不同是有反例的:
http://blog.csdn.net/mengxiang000000/article/details/73718867
考虑先用一边dfs记录子树中覆盖当前节点最浅到哪里,计为。考虑到一个未被覆盖的节点时,覆盖掉中所有的节点,并即可。
没太看懂神犇们的高端写法...只好大力树剖+树状数组维护了。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
struct node {
int to, next;
} edge[MAXN];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node) {j, head[i]}, head[i] = top; }
int siz[MAXN], id[MAXN], ind[MAXN], id_in_ds = 0;
int c[MAXN];
int n;
int ki[MAXN];
int depth[MAXN], fa[MAXN][20];
int low_dep[MAXN];
int dfn[MAXN], dfn_top = 0;
inline int lowbit(int i)
{ return i&(-i); }
void modify(int i, int dt)
{
for (; i <= n; i += lowbit(i))
c[i] += dt;
}
void cover(int i, int j)
{ modify(i, 1), modify(j+1, -1); }
int dat(int i)
{
int ans = 0;
for (; i; i -= lowbit(i))
ans += c[i];
return ans;
}
int kth_ans(int nd, int k)
{
if (k >= depth[nd]) k = depth[nd];
for (register int i = 0; i < 20; i++)
if (k&(1<<i))
nd = fa[nd][i];
return nd;
}
void dfs_init(int nd)
{
siz[nd] = 1;
for (register int i = 1; i < 20; i++)
fa[nd][i] = fa[fa[nd][i-1]][i-1];
low_dep[nd] = kth_ans(nd, ki[nd]-1);
for (int i = head[nd]; i; i = edge[i].next) {
depth[edge[i].to] = depth[nd]+1;
dfs_init(edge[i].to), siz[nd] += siz[edge[i].to];
if (depth[low_dep[edge[i].to]] < depth[low_dep[nd]])
low_dep[nd] = low_dep[edge[i].to];
}
dfn[++dfn_top] = nd;
}
inline bool cmp(int a, int b)
{ return depth[a] > depth[b]; }
void dfs_build(int nd, int from)
{
id[nd] = ++id_in_ds, ind[nd] = from;
int hev = -1;
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (hev == -1 || siz[to] > siz[hev]) hev = to;
}
if (hev == -1) return;
dfs_build(hev, from);
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to != hev) dfs_build(to, to);
}
}
void cover_point(int nd, int pt)
{
while (depth[ind[nd]] > depth[pt]) {
cover(id[ind[nd]], id[nd]);
nd = fa[ind[nd]][0];
}
if (depth[nd] >= depth[pt]) cover(id[pt], id[nd]);
}
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; i++)
scanf("%d", &fa[i][0]), push(fa[i][0], i);
for (int i = 1; i <= n; i++) scanf("%d", &ki[i]);
dfs_init(1);
dfs_build(1, 1);
int cnt = 0;
for (int i = 1; i <= n; i++) {
int pt = dfn[i];
if (dat(id[pt]) != 0) continue;
int anc = low_dep[pt];
cover_point(pt, anc), cnt++;
}
printf("%d\n", cnt);
return 0;
}
填坑...
不知道为什么印象特别深刻去年一次NOIP模拟赛建出二维数点模型,然后Too Naive,并不会做...一直向转化成逆序对...学习了一个偏序那套理论才知道Too Simple..
经典题,一维排序,二维归并,三维树状数组。
注意check一下相同的情况。
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
int a = 0, c;
do c = getchar(); while(!isdigit(c));
while (isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
const int MAXN = 100005;
struct pt {
int x, y, z, id;
} pts[MAXN], tmp[MAXN];
int n, k;
inline bool cmp_x(const pt &a, const pt &b)
{ return a.x < b.x; }
inline bool cmp_y(const pt &a, const pt &b)
{ return a.y < b.y; }
int ans[MAXN];
int c[MAXN*2];
inline int lowbit(int i)
{ return i&(-i); }
void modify(int i, int dt)
{
for (; i <= k; i += lowbit(i))
c[i] += dt;
}
int sum(int i)
{
int ans = 0;
for (; i; i -= lowbit(i))
ans += c[i];
return ans;
}
void solve(int l, int r, int L, int R)
{
if (l >= r) return;
L = pts[l].x, R = pts[r].x;
if (L == R) {
sort(pts+l, pts+r+1, cmp_y);
int lp = l, rp = l;
while (lp <= r) {
while (rp+1 <= r && pts[rp+1].y == pts[lp].y) rp++;
for (int i = lp; i <= rp; i++)
modify(pts[i].z, 1);
for (int i = lp; i <= rp; i++)
ans[pts[i].id] += sum(pts[i].z)-1;
lp = rp = rp+1;
}
for (int i = l; i <= r; i++) modify(pts[i].z, -1);
return;
}
int MID = (L+R)>>1;
int lp = l, rp = r;
for (int i = l; i <= r; i++) {
if (pts[i].x <= MID) tmp[lp++] = pts[i];
else tmp[rp--] = pts[i];
}
int mid = lp-1;
for (int i = l; i <= r; i++)
pts[i] = tmp[i];
solve(l, mid, L, MID), solve(mid+1, r, MID+1, R);
lp = l, rp = mid+1;
int tmp_l = l;
while (lp <= mid && rp <= r) {
if (pts[lp].y <= pts[rp].y) {
modify(pts[lp].z, 1);
tmp[tmp_l++] = pts[lp++];
} else {
ans[pts[rp].id] += sum(pts[rp].z);
tmp[tmp_l++] = pts[rp++];
}
}
while (lp <= mid) {
modify(pts[lp].z, 1);
tmp[tmp_l++] = pts[lp++];
}
while (rp <= r) {
ans[pts[rp].id] += sum(pts[rp].z);
tmp[tmp_l++] = pts[rp++];
}
for (int i = l; i <= mid; i++) modify(pts[i].z, -1);
for (int i = l; i <= r; i++) pts[i] = tmp[i];
}
int d[MAXN];
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
pts[i].x = read(), pts[i].y = read(), pts[i].z = read(), pts[i].id = i;
sort(pts+1, pts+n+1, cmp_x);
solve(1, n, 1, k);
for (int i = 1; i <= n; i++)
d[ans[i]]++;
for (int i = 0; i < n; i++)
printf("%d\n", d[i]);
return 0;
}
填一下这个坑....
考虑向上的路径,一个观测点观测到的条件是:
也就是。因而一个向上的路径的贡献就是其路径上为定值的点。可以在打一个标记,打一个标记。反过来看,对于一个观测点,查询的其实就是子树中所有标记个数减去标记个数。这可以用序+可持久化数组来维护,后者可以使用可持久化线段树或者可持久化平衡树实现(rope也可以),复杂度,理论能拿到95分,然而在Cogs只80...后面还是RE...
#include <bits/stdc++.h>
#include <ext/rope>
using namespace __gnu_cxx;
using namespace std;
const int MAXN = 300005;
struct node {
int to, next;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node){j, head[i]}, head[i] = top; }
int depth[MAXN], W[MAXN];
int u[MAXN*2], v[MAXN*2], id[MAXN*2];
int n, m;
int lca[MAXN], vis[MAXN];
int fa[MAXN], father[MAXN], ans[MAXN];
inline int findf(int nd)
{ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }
inline int read()
{
int a = 0, c;
do c = getchar(); while(!isdigit(c));
while (isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
vector<int> pt[MAXN];
struct tag {
int dat;
int tp;
};
vector<tag> tag_up[MAXN], tag_down[MAXN];
void dfs_init(int nd, int f)
{
vis[nd] = 1, father[nd] = f;
for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)
if (vis[v[*i]]) {
lca[id[*i]] = findf(v[*i]);
}
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f) continue;
depth[to] = depth[nd]+1, dfs_init(to, nd);
}
fa[nd] = f;
}
void put_tag()
{
for (int i = 1; i <= m; i++) {
int c = lca[i], s = u[i], t = v[i];
if (c == t) {
tag_up[s].push_back((tag){depth[s], 1});
tag_up[father[c]].push_back((tag){depth[s], -1});
} else if (c == s) {
tag_down[t].push_back((tag){-depth[s], 1});
tag_down[father[c]].push_back((tag){-depth[s], -1});
} else {
tag_up[s].push_back((tag){depth[s], 1});
tag_up[father[c]].push_back((tag){depth[s], -1});
int beg = depth[s]-depth[c];
tag_down[t].push_back((tag){beg-depth[c], 1});
tag_down[c].push_back((tag){beg-depth[c], -1});
}
}
}
int dfn[MAXN], dfn_top = 0, out[MAXN];
struct my_array {
rope<int> *arr[MAXN];
int range_l, range_r, lev;
void build()
{
arr[0] = new rope<int>;
lev = 0;
for (int i = range_l; i <= range_r; i++)
arr[0]->push_back(0);
}
inline void new_lev()
{ ++lev, arr[lev] = new rope<int>(*arr[lev-1]); }
inline void add_pos(int pos, int dt)
{
pos = pos-range_l;
int dat = arr[lev]->at(pos)+dt;
arr[lev]->replace(pos, dat);
}
inline int segment_sum(int l, int r, int pos)
{ return arr[r]->at(pos-range_l)-arr[l-1]->at(pos-range_l); }
} arr_up, arr_down;
void dfs_travel(int nd)
{
dfn[nd] = ++dfn_top;
arr_up.new_lev(), arr_down.new_lev();
for (register vector<tag>::iterator i = tag_up[nd].begin(); i != tag_up[nd].end(); ++i)
arr_up.add_pos(i->dat, i->tp);
for (register vector<tag>::iterator i = tag_down[nd].begin(); i != tag_down[nd].end(); ++i)
arr_down.add_pos(i->dat, i->tp);
for (register int i = head[nd]; i; i = edge[i].next)
if (edge[i].to != father[nd]) dfs_travel(edge[i].to);
out[nd] = dfn_top;
}
int main()
{
freopen("runninga.in", "r", stdin);
freopen("runninga.out", "w", stdout);
int size = 128 << 20;
char *p = (char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p));
n = read(), m = read();
for (register int i = 1; i < n; i++) {
int x = read(), y = read();
push(x, y), push(y, x);
}
for (register int i = 1; i <= n; i++) W[i] = read();
for (register int i = 1; i <= m; i++) {
u[i] = read(), v[i] = read();
u[m+i] = v[i], v[m+i] = u[i], id[m+i] = id[i] = i;
pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);
}
dfs_init(1, 0);
put_tag();
arr_up.range_l = 0, arr_up.range_r = 2*n+1, arr_down.range_l = -n-1, arr_down.range_r = n+1;
arr_up.build(), arr_down.build();
dfs_travel(1);
for (register int i = 1; i <= n; i++) {
ans[i] += arr_up.segment_sum(dfn[i], out[i], depth[i]+W[i]);
ans[i] += arr_down.segment_sum(dfn[i], out[i], W[i]-depth[i]);
}
for (register int i = 1; i <= n; i++)
printf("%d ", ans[i]);
puts("");
return 0;
}
然而这题就是典型“会的越多得分越低”型题目...这个可持久化维护的特别鸡肋。由于答案具有可减性,我们在dfs的过程中维护一个数组,进入时记录一下答案,退出时记录一下,差值就是所求了...
#include <bits/stdc++.h>
#include <ext/rope>
using namespace __gnu_cxx;
using namespace std;
const int MAXN = 300005;
struct node {
int to, next;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node){j, head[i]}, head[i] = top; }
int depth[MAXN], W[MAXN];
int u[MAXN*2], v[MAXN*2], id[MAXN*2];
int n, m;
int lca[MAXN], vis[MAXN];
int fa[MAXN], father[MAXN], ans[MAXN];
inline int findf(int nd)
{ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }
inline int read()
{
int a = 0, c;
do c = getchar(); while(!isdigit(c));
while (isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
vector<int> pt[MAXN];
struct tag {
int dat;
int tp;
};
vector<tag> tag_up[MAXN], tag_down[MAXN];
void dfs_init(int nd, int f)
{
vis[nd] = 1, father[nd] = f;
for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)
if (vis[v[*i]]) {
//cerr << *i << " " << id[*i] << " " << v[*i] << endl;
lca[id[*i]] = findf(v[*i]);
}
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f) continue;
depth[to] = depth[nd]+1, dfs_init(to, nd);
}
fa[nd] = f;
}
void put_tag()
{
for (int i = 1; i <= m; i++) {
int c = lca[i], s = u[i], t = v[i];
// cerr << "LCA(" << s << "," << t << ") = " << c << endl;
if (c == t) {
tag_up[s].push_back((tag){depth[s], 1});
tag_up[father[c]].push_back((tag){depth[s], -1});
} else if (c == s) {
tag_down[t].push_back((tag){-depth[s], 1});
tag_down[father[c]].push_back((tag){-depth[s], -1});
} else {
tag_up[s].push_back((tag){depth[s], 1});
tag_up[father[c]].push_back((tag){depth[s], -1});
int beg = depth[s]-depth[c];
tag_down[t].push_back((tag){beg-depth[c], 1});
tag_down[c].push_back((tag){beg-depth[c], -1});
}
}
}
int arr_up[MAXN*4], arr_down[MAXN*4], beg = 600000;
void dfs_travel(int nd)
{
int pre = arr_up[depth[nd]+W[nd]+beg] + arr_down[W[nd]-depth[nd]+beg];
for (register int i = head[nd]; i; i = edge[i].next)
if (edge[i].to != father[nd]) dfs_travel(edge[i].to);
for (register vector<tag>::iterator i = tag_up[nd].begin(); i != tag_up[nd].end(); ++i)
arr_up[i->dat+beg] += i->tp;
for (register vector<tag>::iterator i = tag_down[nd].begin(); i != tag_down[nd].end(); ++i)
arr_down[i->dat+beg] += i->tp;
ans[nd] = arr_up[depth[nd]+W[nd]+beg] + arr_down[W[nd]-depth[nd]+beg]-pre;
}
int main()
{
n = read(), m = read();
for (register int i = 1; i < n; i++) {
int x = read(), y = read();
push(x, y), push(y, x);
}
for (register int i = 1; i <= n; i++) W[i] = read();
for (register int i = 1; i <= m; i++) {
u[i] = read(), v[i] = read();
u[m+i] = v[i], v[m+i] = u[i], id[m+i] = id[i] = i;
pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);
}
dfs_init(1, 0);
put_tag();
dfs_travel(1);
for (register int i = 1; i <= n; i++)
printf("%d ", ans[i]);
puts("");
return 0;
}
继续填古代坑计划...
当时用SPFA水了25...
现在看来很水啊..二分后随便树剖就行了。也可以树上差分搞。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
struct node {
int to, next, dis;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j, int d)
{ edge[++top] = (node) {j, head[i], d}, head[i] = top; }
int fa[MAXN];
inline int findf(int a)
{ return fa[a]?fa[a] = findf(fa[a]):a; }
int u[MAXN*2], v[MAXN*2], id[MAXN*2], lca[MAXN], vis[MAXN], father[MAXN];
int dis[MAXN];
int n, m;
vector<int> pt[MAXN];
int depth[MAXN], len[MAXN];
inline int read()
{
int a = 0, c;
do c = getchar(); while (!isdigit(c));
while (isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
void dfs_init(int nd, int f)
{
vis[nd] = 1, father[nd] = f;
for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)
if (vis[v[*i]])
lca[id[*i]] = findf(v[*i]);
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f) continue;
depth[to] = depth[nd]+1, len[to] = len[nd]+edge[i].dis;
dfs_init(to, nd);
}
fa[nd] = f;
}
int tag[MAXN], max_able = 0;
void dfs_collect(int nd)
{
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == father[nd]) continue;
dfs_collect(to), tag[nd] += tag[to];
}
}
bool check(int ans)
{
memset(tag, 0, sizeof tag);
int max_val = 0, cnt = 0;
max_able = 0;
for (int i = 1; i <= m; i++)
if (dis[i] > ans) {
max_val = max(max_val, dis[i]);
cnt++;
// cerr << u[i] << " " << v[i] << " " << lca[i] << endl;
tag[u[i]]++, tag[v[i]]++, tag[father[lca[i]]]--, tag[lca[i]]--;
}
if (cnt == 0) return 1;
//cerr << cnt << endl;
dfs_collect(1);
for (int i = 1; i <= n; i++) {
if (tag[i] != cnt) continue;
//cerr << tag[i] << endl;
for (int j = head[i]; j; j = edge[j].next) {
int to = edge[j].to;
// if (to == father[i]) continue;
if (tag[to] == cnt)
max_able = max(max_able, edge[j].dis);
}
}
//cerr << max_val << " " << max_able << " " << ans << endl;
return max_val-max_able <= ans;
}
int main()
{
//freopen("transport.in", "r", stdin);
//freopen("transport.out", "w", stdout);
n = read(), m = read();
for (int i = 1; i < n; i++) {
int l = read(), r = read(), d = read();
push(l, r, d), push(r, l, d);
}
for (int i = 1; i <= m; i++) {
u[i] = read(), v[i] = read();
v[i+m] = u[i], u[i+m] = v[i], id[i] = id[i+m] = i;
pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);
}
dfs_init(1, 0);
for (int i = 1; i <= m; i++)
dis[i] = len[u[i]]+len[v[i]]-len[lca[i]]*2;
int l = 0, r = 300000ll*1000, mid;
while (l <= r) {
mid = (l+r)>>1;
if (check(mid)) r = mid-1;
else l = mid+1;
}
printf("%d\n", r+1);
return 0;
}
我SX....原来括号序列就是dfs序啊..
二分答案,维护括号序列hash,考虑对于节点,以其为根的子树恰好被删去即是在其个祖先处,打一个标记,然后对于每个节点大力计算就好了。复杂度,如果祖先用长链剖分、排序用基数排序,可以做到
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
struct node {
int to, next;
} edge[MAXN];
int head[MAXN], top = 0;
void push(int i, int j)
{ edge[++top] = (node) {j, head[i]}, head[i] = top; }
int hash_dat[MAXN*2], dfn_top = 0;
vector<pair<int, int> > forbidden[MAXN];
int fa[MAXN][20], n;
int dfn_in[MAXN], dfn_out[MAXN];
typedef unsigned long long ull;
ull hash_tab[MAXN*2], bs = 19260817, power[MAXN*2];
int height[MAXN];
// --- hash ---
void hash_init()
{
power[0] = 1;
for (int i = 1; i <= n*2; i++) power[i] = power[i-1]*bs;
for (int i = 1; i <= n*2; i++)
hash_tab[i] = hash_tab[i-1]*bs+hash_dat[i];
}
ull hash_val(int l, int r)
{ return hash_tab[r]-hash_tab[l-1]*power[r-l+1]; }
// --- hash ---
int kth_anc(int nd, int k)
{
for (int i = 0; i < 20; i++)
if (k&(1<<i))
nd = fa[nd][i];
return nd;
}
void dfs_init(int nd)
{
height[nd] = 0;
++dfn_top, hash_dat[dfn_top] = 1;
dfn_in[nd] = dfn_top;
for (int i = 1; i < 20; i++)
fa[nd][i] = fa[fa[nd][i-1]][i-1];
for (int i = head[nd]; i; i = edge[i].next) {
fa[edge[i].to][0] = nd, dfs_init(edge[i].to);
height[nd] = max(height[nd], height[edge[i].to]+1);
}
++dfn_top, hash_dat[dfn_top] = 2;
dfn_out[nd] = dfn_top;
}
set<ull> st;
bool calc(int k)
{
for (int i = 1; i <= n; i++) forbidden[i].clear();
st.clear();
for (int i = 1; i <= n; i++) {
int nd = kth_anc(i, k+1);
forbidden[nd].push_back(make_pair(dfn_in[i], dfn_out[i]));
}
for (int i = 1; i <= n; i++) {
sort(forbidden[i].begin(), forbidden[i].end());
forbidden[i].push_back(make_pair(dfn_out[i]+1, dfn_out[i]+1));
}
for (int i = 1; i <= n; i++) {
if (height[i] < k) continue;
ull hash_v = 0;
int l = dfn_in[i];
for (int j = 0, sz = forbidden[i].size(); j < sz; j++) {
int r = forbidden[i][j].first-1;
if (l <= r)
hash_v = hash_v*power[r-l+1]+hash_val(l, r);
l = forbidden[i][j].second+1;
}
if (st.count(hash_v)) return 1;
st.insert(hash_v);
}
return 0;
}
inline int read()
{
int a = 0, c;
do c = getchar(); while (!isdigit(c));
while (isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
int rd[MAXN], rt;
int main()
{
n = read();
for (int i = 1; i <= n; i++) {
int x, v;
x = read();
for (int j = 1; j <= x; j++)
v = read(), push(i, v), rd[v]++;
}
for (int i = 1; i <= n; i++)
if (rd[i] == 0) {
rt = i;
break;
}
dfs_init(rt), hash_init();
int l = 0, r = n, mid;
while (l <= r) {
mid = (l+r)>>1;
if (calc(mid)) l = mid+1;
else r = mid-1;
}
printf("%d\n", l-1);
return 0;
}
这个命题思路在哪里见过....当时是kmp,现在用SAM就好了。
考虑枚举矩形的高度,则高度为的矩形可以看成以维向量为字符集的长度为的字符串。
人话就是,把一列看成一个字符。
然后就可以用广义后缀自动机大法了。由于字符集太大,需要hash+map<ull>
。
ps : std貌似是后缀数组,复杂度少一个,速度快到不知哪里去了...
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
int n, m;
char A[115][115];
const int MAXN = 30005;
const ull bas = 19260817;
struct SAM {
map<ull,int> chl[MAXN];
int fa[MAXN], maxl[MAXN], top, root;
void clear()
{ top = root = 1, maxl[1] = 0; }
int push(int nd, ull dat)
{
int p = nd, np = ++top; maxl[np] = maxl[p]+1, chl[np].clear();
while (p && !chl[p][dat]) chl[p][dat] = np, p = fa[p];
if (!p) fa[np] = root;
else {
int q = chl[p][dat];
if (maxl[q] == maxl[p]+1) fa[np] = q;
else {
int nq = ++top; chl[nq] = chl[q], maxl[nq] = maxl[p]+1;
fa[nq] = fa[q], fa[q] = fa[np] = nq;
while (p && chl[p][dat] == q) chl[p][dat] = nq, p = fa[p];
}
}
return np;
}
} sam_main;
struct trie {
map<ull, int> tree[MAXN];
int top, root;
trie()
{ top = root = 1; }
void clear()
{
for (int i = 1; i <= top; i++) tree[i].clear();
top = root = 1;
}
void push(vector<ull> &str)
{
int nd = root;
for (auto i : str) {
if (!tree[nd].count(i)) tree[nd][i] = ++top;
nd = tree[nd][i];
}
}
} trie_main;
queue<int> que;
int st[MAXN];
void bfs_build_sam()
{
sam_main.clear();
que.push(trie_main.root);
st[trie_main.root] = sam_main.root;
while (!que.empty()) {
int nd = que.front(); que.pop();
for (auto i : trie_main.tree[nd]) {
st[i.second] = sam_main.push(st[nd], i.first);
que.push(i.second);
}
}
}
int solve()
{
bfs_build_sam();
int ans = 0;
for (int i = 2; i <= sam_main.top; i++)
ans += sam_main.maxl[i]-sam_main.maxl[sam_main.fa[i]];
return ans;
}
ull hash_val[120][120]; // i, j 第i列前j项哈希
ull power[120];
inline ull hash_seg(int i, int l, int r)
{ return hash_val[i][r]-hash_val[i][l-1]*power[r-l+1]; }
int main()
{
scanf("%d%d", &n, &m);
power[0] = 1;
for (int i = 1; i <= n; i++) power[i] = power[i-1]*bas;
for (int i = 1; i <= n; i++)
scanf("%s", A[i]+1);
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++)
hash_val[i][j] = hash_val[i][j-1]*bas+A[j][i]-'A'+1;
}
vector<ull> vec;
int ans = 0;
for (int i = 1; i <= n; i++) {
trie_main.clear();
for (int j = 1; j+i-1 <= n; j++) {
vec.clear();
for (int k = 1; k <= m; k++)
vec.push_back(hash_seg(k, j, j+i-1));
trie_main.push(vec);
}
ans += solve();
}
cout << ans << endl;
return 0;
}
看不懂神犇五行代码系列qwq...
考虑展开右边级数第项,发现:
然后就py直接做了...
import math
def choose(n, k):
ans = 1
for i in range(k):
ans = ans*(n-i)
for i in range(1, k+1):
ans = ans/i
return ans
n = int(raw_input())
t = int(raw_input())
m = int(raw_input())
def mul(a, b):
c = {}
for i in range(2):
for j in range(2):
c[i, j] = 0
for k in range(2):
c[i, j] = (c[i, j]+a[i, k]*b[k, j])%3389
return c
def power(a, n):
ans = {(0,0):1, (1,1):1, (1, 0):0, (0,1):0}
for i in range(10000):
if (((n>>i)&1) != 0):
ans = mul(ans, a)
a = mul(a, a)
return ans
def get_A(n):
c = {}
c[0, 0], c[0, 1], c[1, 0], c[1, 1] = 1234, 5678, 0, 1
c = power(c, n)
return (c[0, 0]+c[0, 1])%3389
b = {}
b[0] = get_A(n)
for i in range(1, 6):
tmp, flag, tt = n-i, 1, t
b[i] = get_A(n-i)
for j in range(1, i+1):
b[i] = b[i]+b[i-j]*choose(tmp+j, j)*t**j*flag
flag, tt = -flag, tt*t
# print(b[i])
print(b[n-m])
补题计划continue...
首先考场是肯定写85-95的...后面的性价比不高..
正解其实也(看过题解后)比较简单。考虑求的个数只要求出形如的结尾位置和开头位置,然后即为所求。考虑枚举的长度,并将作为关键点。对于一个方案可以转化成求两个关键点和。如果,说明存在一个拆分,在对应的位置区间加1。求lcp和lcs可以用后缀数组或者二分哈希(单哈希会被卡..双哈希在uoj貌似TLE...但loj和bzoj过了)。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
typedef unsigned long long ull;
char str[MAXN];
int n, T;
ull hash_tab[MAXN], bas = 31;
long long hash_tab2[MAXN], bas2 = 97, mod = 1e9+7;
ull power[MAXN];
long long power2[MAXN];
void hash_init()
{
for (register int i = 1; i <= n+n; i++)
hash_tab[i] = hash_tab[i-1]*bas+str[i]-'a'+1;
for (register int i = 1; i <= n+n; i++)
hash_tab2[i] = (hash_tab2[i-1]*bas2%mod+str[i]-'a'+1)%mod;
}
inline ull hash_val1(int l, int r)
{ return l <= r ? hash_tab[r]-hash_tab[l-1]*power[r-l+1] : 0; }
inline ull hash_val2(int l, int r)
{ return l <= r ? ((hash_tab2[r]-hash_tab2[l-1]*power2[r-l+1]%mod)%mod+mod)%mod : 0; }
int tag_after[MAXN], tag_bef[MAXN];
int pos[MAXN], top = 0;
int max_len_lr(int a, int b, int upp)
{
register int l = 0, r = upp, mid;
while (l <= r) {
mid = (l+r)>>1;
if (hash_val1(a, a+mid-1) == hash_val1(b, b+mid-1)
&& hash_val2(a, a+mid-1) == hash_val2(b, b+mid-1)) l = mid+1;
else r = mid-1;
}
return l-1;
}
int max_len_rl(int a, int b, int upp)
{
register int l = 0, r = upp, mid;
while (l <= r) {
mid = (l+r)>>1;
if (hash_val1(a-mid+1, a) == hash_val1(b-mid+1, b)
&& hash_val2(a-mid+1, a) == hash_val2(b-mid+1, b)) l = mid+1;
else r = mid-1;
}
return l-1;
}
int main()
{
scanf("%d", &T);
while (T--) {
scanf("%s", str+1);
memset(tag_bef, 0, sizeof tag_bef);
memset(tag_after, 0, sizeof tag_after);
n = strlen(str+1);
power[0] = power2[0] = 1;
for (register int i = 1; i <= n; i++) power[i] = power[i-1]*bas;
for (register int i = 1; i <= n; i++) power2[i] = power2[i-1]*bas2%mod;
for (register int i = n+1; i <= n+n; i++) str[i] = int('z')+1;
hash_init();
for (register int i = 1; i <= n; i++) {
top = 0;
do {
++top, pos[top] = pos[top-1]+i;
} while (pos[top] < n);
register int a, b, lr, rl;
for (register int j = 1; j < top; j++) {
a = pos[j], b = pos[j+1];
lr = max_len_lr(a, b, i), rl = max_len_rl(a, b, i);
if (lr+rl-1 < i) continue;
tag_after[b+i-rl]++, tag_after[b+lr]--;
tag_bef[a-(i-lr)+1]--, tag_bef[a-rl+1]++;
}
}
for (register int i = 1; i <= n+n; i++) tag_after[i] += tag_after[i-1], tag_bef[i] += tag_bef[i-1];
long long ans = 0;
for (register int i = 1; i <= n; i++)
ans += (long long)tag_after[i]*tag_bef[i+1];
printf("%lld\n", ans);
}
return 0;
}
好神啊....感觉在考场上最多推出84...
首先打个表发现所求其实就是:
然后交换求和顺序并莫比乌斯大力算,得出:
设,考虑这样一个序列:
由辗转相除法可以得出,因此这段和这段对答案的贡献是一样的。所以:
这样就可以大力拿到84了.
剩下的比较有构造性...设。如果能快速求出这个就可以喜闻乐见下底函数分块了。
考虑最小的因子,令为最大的,则考虑。则:
由可知。而,因此:
大功告成...
#include <bits/stdc++.h>
using namespace std;
const int MAXK = 2005, MAXN = 1000005;
const int MOD = 1000007, bas = 2333;
int cnt = 0;
struct hash_table {
long long tab[MOD];
int n[MOD], k[MOD];
inline int hash_val(int n, int k)
{ return ((long long)n*bas%MOD+k)%MOD; }
inline long long val(int __n, int __k)
{
int val = hash_val(__n, __k);
while (n[val] != __n || k[val] != __k) (val += 1) %= MOD;
return tab[val];
}
inline void insert(int __n, int __k, long long dat)
{
int val = hash_val(__n, __k);
while (k[val] != 0 || n[val] != 0) val++;
n[val] = __n, k[val] = __k, tab[val] = dat;
}
inline bool count(int __n, int __k)
{
int val = hash_val(__n, __k);
while (n[val] != __n || k[val] != __k) {
if (n[val] == 0 && k[val] == 0) return 0;
val++;
}
return 1;
}
};
inline int gcd(int a, int b)
{ return b == 0 ? a : gcd(b, a%b); }
int prime[MAXN], not_prime[MAXN], mu[MAXN], top = 0;
int M[MAXN];
int n, m, k;
int p[MAXK], q[MAXK]; // 拿出的第一个素因子; 剩余
int f[MAXK];
void get_prime(int n)
{
mu[1] = 1, not_prime[1] = 1;
for (int i = 2; i <= n; i++) {
if (!not_prime[i]) prime[++top] = i, mu[i] = -1;
for (int j = 1; j <= top && i*prime[j] <= n; j++) {
not_prime[i*prime[j]] = 1;
if (i%prime[j] == 0) {
mu[i*prime[j]] = 0;
break;
}
mu[i*prime[j]] = -mu[i];
}
}
for (int i = 1; i <= n; i++)
M[i] = M[i-1]+mu[i];
}
hash_table hash_M;
long long get_M(int N)
{
if (N < MAXN) return M[N];
if (hash_M.count(N, 0)) return hash_M.val(N, 0);
long long ans = 1;
int last;
for (int i = 2; i <= N; i = last+1) {
last = N/(N/i);
ans -= (last-i+1)*get_M(N/i);
}
hash_M.insert(N, 0, ans);
return ans;
}
hash_table hash_s;
long long get_G(int n, int k)
{
// cerr << n << " " << k << endl;
if (n == 0) return 0;
if (k == 1) return get_M(n);
if (hash_s.count(n, k)) return cnt++, hash_s.val(n, k);
long long tmp = get_G(n, q[k])+get_G(n/p[k], p[k]*q[k]);
hash_s.insert(n, k, tmp);
return tmp;
}
void init_pq()
{
for (int i = 2; i <= k; i++) {
if (!not_prime[i]) p[i] = i, q[i] = 1;
else {
int fac, pp = 1;
for (int j = 1; j <= top; j++)
if (i%prime[j] == 0) {
fac = prime[j];
break;
}
while (i%(pp*fac) == 0) pp *= fac;
p[i] = fac, q[i] = i/pp;
}
}
}
void init_f()
{
f[0] = 0;
for (int i = 1; i <= k; i++)
f[i] = f[i-1]+(gcd(i, k) == 1);
}
inline int get_F(int n)
{ return n/k*f[k]+f[n%k]; }
int main()
{
cin >> n >> m >> k;
get_prime(MAXN-1);
init_pq();
init_f();
int last;
long long ans = 0;
for (register int i = 1; i <= n && i <= m; i = last+1) {
last = min(min(n/(n/i), m/(m/i)), n);
ans += (get_G(last, k)-get_G(i-1, k))*(n/i)*get_F(m/i);
}
cout << ans << endl;
return 0;
}
可能是NOI2016唯一一道水题吧......
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500005;
struct segm {
int l, r, len;
friend bool operator < (const segm &a, const segm &b)
{ return a.len < b.len; }
} seg[MAXN];
int n, m;
int dx[MAXN*2], dx_top = 0;
void dx_init()
{
for (int i = 1; i <= n; i++)
dx[++dx_top] = seg[i].l, dx[++dx_top] = seg[i].r;
sort(dx+1, dx+dx_top+1);
dx_top = unique(dx+1, dx+dx_top+1)-dx-1;
for (int i = 1; i <= n; i++) {
seg[i].l = lower_bound(dx+1, dx+dx_top+1, seg[i].l)-dx;
seg[i].r = lower_bound(dx+1, dx+dx_top+1, seg[i].r)-dx;
}
}
int max_val[MAXN*4], lc[MAXN*4], rc[MAXN*4], l[MAXN*4], r[MAXN*4], tag[MAXN*4];
int root = 0, top = 0;
inline void pdw(int nd)
{
max_val[nd] += tag[nd];
if (lc[nd]) tag[lc[nd]] += tag[nd], tag[rc[nd]] += tag[nd];
tag[nd] = 0;
}
inline void update(int nd)
{ max_val[nd] = max(max_val[lc[nd]], max_val[rc[nd]]); }
void build(int &nd, int opl, int opr)
{
nd = ++top, l[nd] = opl, r[nd] = opr;
if (opl < opr)
build(lc[nd], opl, (opl+opr)/2), build(rc[nd], (opl+opr)/2+1, opr);
}
void modify(int nd, int opl, int opr, int dt)
{
pdw(nd);
if (l[nd] == opl && r[nd] == opr) tag[nd] += dt;
else {
int mid = (l[nd] + r[nd]) >> 1;
if (opr <= mid) modify(lc[nd], opl, opr, dt);
else if (opl > mid) modify(rc[nd], opl, opr, dt);
else modify(lc[nd], opl, mid, dt), modify(rc[nd], mid+1, opr, dt);
pdw(lc[nd]), pdw(rc[nd]), update(nd);
}
}
int query(int nd, int opl, int opr)
{
pdw(nd);
if (l[nd] == opl && r[nd] == opr) return max_val[nd];
else {
int mid = (l[nd] + r[nd]) >> 1;
if (opr <= mid) return query(lc[nd], opl, opr);
else if (opl > mid) return query(rc[nd], opl, opr);
else return max(query(lc[nd], opl, mid), query(rc[nd], mid+1, opr));
}
}
inline int read()
{
int a = 0, c;
do c = getchar(); while (!isdigit(c));
while (isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= n; i++) {
seg[i].l = read();
seg[i].r = read();
seg[i].len = seg[i].r-seg[i].l;
}
sort(seg+1, seg+n+1);
dx_init();
build(root, 1, dx_top);
int ans = INT_MAX;
int l = 1, r = 0;
while (r < n) {
while (r < n && query(root, 1, dx_top) < m)
++r, modify(root, seg[r].l, seg[r].r, 1);
if (query(root, 1, dx_top) >= m)
ans = min(ans, seg[r].len-seg[l].len);
while (l <= n && query(root, 1, dx_top) >= m) {
if (query(root, 1, dx_top) >= m)
ans = min(ans, seg[r].len-seg[l].len);
modify(root, seg[l].l, seg[l].r, -1);
l++;
}
if (query(root, 1, dx_top) >= m)
ans = min(ans, seg[r].len-seg[l].len);
}
if (ans == INT_MAX)
puts("-1");
else printf("%d\n", ans);
return 0;
}
每个数大于的质因子最多只有一个,然后把相同的化为一类,大力分类讨论dp即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 505;
int n;
long long P;
int prime[] = {2, 3, 5, 7, 11, 13, 17, 19};
long long dp[2][1<<8|1][1<<8|1];
long long p[2][2][1<<8|1][1<<8|1];
vector<int> kind[MAXN];
int main()
{
scanf("%d%lld", &n, &P);
for (int i = 2; i <= n; i++) {
int S = 0, num = i;
for (int j = 0; j < 8; j++)
if (num%prime[j] == 0) {
S |= 1<<j;
while (num%prime[j] == 0) num /= prime[j];
}
if (num == 1) kind[n+1].push_back(S);
else kind[num].push_back(S);
}
int now = 0, nxt = 1;
dp[now][0][0] = 1;
for (register int i = 0; i <= n; i++) {
int nw = 0, nx = 1;
if (kind[i].empty()) continue;
memcpy(p[0][nw], dp[now], sizeof dp[now]);
memset(p[0][nx], 0, sizeof p[0][nx]);
memcpy(p[1][nw], dp[now], sizeof dp[now]);
memset(p[1][nx], 0, sizeof p[1][nx]);
for (register vector<int>::iterator l = kind[i].begin(); l != kind[i].end(); ++l) {
//cerr << i << " " << *l << endl;
for (register int j = 0; j < 1<<8; j++)
for (register int k = 0; k < 1<<8; k++) {
(p[0][nx][j][k] += p[0][nw][j][k]) %= P;
(p[1][nx][j][k] += p[1][nw][j][k]) %= P;
(p[0][nx][j|(*l)][k] += p[0][nw][j][k]) %= P;
(p[1][nx][j][k|(*l)] += p[1][nw][j][k]) %= P;
}
memset(p[0][nw], 0, sizeof p[0][nw]);
memset(p[1][nw], 0, sizeof p[1][nw]);
swap(nw, nx);
}
for (register int j = 0; j < 1<<8; j++)
for (register int k = 0; k < 1<<8; k++)
dp[now][j][k] = (p[0][nw][j][k]+p[1][nw][j][k]-dp[now][j][k])%P;
}
for (register int i = 1; i <= kind[n+1].size(); i++) {
int S = kind[n+1][i-1];
for (register int j = 0; j < 1<<8; j++)
for (register int k = 0; k < 1<<8; k++) {
long long val = dp[now][j][k];
(dp[nxt][j][k] += val) %= P;
(dp[nxt][j][k|S] += val) %= P;
(dp[nxt][j|S][k] += val) %= P;
}
memset(dp[now], 0, sizeof dp[now]);
swap(now, nxt);
}
long long ans = 0;
for (register int j = 0; j < 1<<8; j++) {
for (register int k = 0; k < 1<<8; k++) {
if ((j&k) == 0)
(ans += dp[now][j][k]) %= P;
//cerr << dp[now][j][k] << " ";
}
//cerr << endl;
}
printf("%lld\n", (ans+P)%P);
return 0;
}
真...哈夫曼编码裸题
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100050;
long long w[MAXN];
int n, k;
long long tot_len = 0;
struct pt {
long long val;
int lev;
friend bool operator < (const pt &a, const pt &b)
{ return a.val == b.val ? a.lev > b.lev : a.val > b.val; }
};
priority_queue<pt> heap;
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%lld", &w[i]);
while ((n-1)%(k-1) != 0) w[++n] = 0;
for (int i = 1; i <= n; i++) heap.push((pt){w[i], 0});
while (heap.size() > 1) {
long long val = 0;
int lev = 0;
for (int i = 1; i <= k; i++) {
pt a = heap.top(); heap.pop();
val += a.val, lev = max(lev, a.lev);
}
tot_len += val;
heap.push((pt){val, lev+1});
}
cout << tot_len << endl << heap.top().lev << endl;
return 0;
}
LCT维护广义后缀自动机Right集合大小.......................
太TM难写了啊...
「不过貌似用splay维护dfs序要好写一点...什么时候尝试一个」
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
struct link_cut_trees {
int chl[MAXN][2], fa[MAXN], rev[MAXN];
int tag[MAXN], dat[MAXN];
int stk[MAXN], top;
bool is_rt(int x)
{ return chl[fa[x]][0] != x && chl[fa[x]][1] != x;}
inline void pdw(int nd)
{
if (chl[nd][0]) tag[chl[nd][0]] += tag[nd], rev[chl[nd][0]] ^= rev[nd];
if (chl[nd][1]) tag[chl[nd][1]] += tag[nd], rev[chl[nd][1]] ^= rev[nd];
if (rev[nd]) swap(chl[nd][0], chl[nd][1]);
dat[nd] += tag[nd];
rev[nd] = 0, tag[nd] = 0;
}
void zig(int nd)
{
int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;
int son = chl[nd][tp^1];
if (son) fa[son] = p; if (!is_rt(p)) chl[g][tg] = nd;
fa[nd] = g, fa[p] = nd, chl[nd][tp^1] = p, chl[p][tp] = son;
}
void splay(int nd)
{
stk[top = 1] = nd;
for (int i = nd; !is_rt(i); i = fa[i]) stk[++top] = fa[i];
while (top) pdw(stk[top--]);
while (!is_rt(nd)) {
int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;
if (is_rt(p)) { zig(nd); break; }
if (tp == tg) zig(p), zig(nd);
else zig(nd), zig(nd);
}
}
void access(int x)
{
for (int y = 0; x; x = fa[y = x])
splay(x), chl[x][1] = y;
}
void make_rt(int x)
{ access(x), splay(x), rev[x] ^= 1; }
void link(int x, int y) // x --> y
{
fa[x] = y, access(y), splay(y), tag[y] += dat[x];
}
void cut(int x)
{
access(x), splay(x), tag[chl[x][0]] -= dat[x];
fa[chl[x][0]] = 0, chl[x][0] = 0;
}
int get_dat(int x)
{
access(x), splay(x);
return dat[x];
}
} ;
struct SAM {
int chl[MAXN][3], fa[MAXN], maxl[MAXN], top, root;
link_cut_trees LCT;
long long ans;
SAM()
{ top = root = 1, ans = 0; }
int push(int nd, int x)
{
int p = nd, np = ++top; LCT.dat[np] = 1, maxl[np] = maxl[p]+1;
while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
if (!p) fa[np] = root, ans += maxl[np]-maxl[fa[np]], LCT.link(np, root);
else {
int q = chl[p][x];
if (maxl[q] == maxl[p]+1) fa[np] = q, ans += maxl[np]-maxl[fa[np]], LCT.link(np, q);
else {
int nq = ++top; maxl[nq] = maxl[p]+1;
memcpy(chl[nq], chl[q], sizeof chl[q]);
fa[nq] = fa[q], LCT.link(nq, fa[nq]);
ans += maxl[nq]-maxl[fa[nq]];
ans -= maxl[q]-maxl[fa[q]];
fa[q] = fa[np] = nq;
ans += maxl[q]-maxl[fa[q]], ans += maxl[np]-maxl[fa[np]];
LCT.cut(q), LCT.cut(np), LCT.link(np, nq), LCT.link(q, nq);
while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
}
}
return np;
}
int get_pt(int nd, const char *str)
{
if (*str == '\0') return nd;
if (!chl[nd][*str-'a']) return -1;
return get_pt(chl[nd][*str-'a'], str+1);
}
inline long long diff_substring()
{ return ans; }
inline int match_times(const char *str)
{
int nd = get_pt(root, str);
if (nd == -1) return 0;
return LCT.get_dat(nd);
}
};
struct trie {
struct node {
int to, next;
char dis;
} edge[MAXN*2];
int head[MAXN], top;
inline void push(int i, int j, char c)
{ edge[++top] = (node){j, head[i], c}, head[i] = top; }
int stat[MAXN], vis[MAXN];
int root;
SAM autometa;
queue<int> que;
trie()
{ root = 1, stat[root] = 1, top = 0; }
void dfs(int x)
{
vis[x] = 1;
for (int i = head[x]; i; i = edge[i].next) {
int to = edge[i].to, c = edge[i].dis-'a';
if (vis[to]) continue;
stat[to] = autometa.push(stat[x], c);
dfs(to);
}
}
inline int match_times(const char *str)
{ return autometa.match_times(str); }
inline long long diff_substring()
{ return autometa.diff_substring(); }
} T;
int n, m;
int id;
char str[MAXN];
int main()
{
scanf("%d%d", &id, &n);
for (int i = 1; i < n; i++) {
int u, v; char c;
scanf("%d%d %c\n", &u, &v, &c);
T.push(u, v, c), T.push(v, u, c);
}
T.dfs(1);
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
int opt, r, t;
scanf("%d", &opt);
if (opt == 1) {
printf("%lld\n", T.diff_substring());
} else if (opt == 2) {
scanf("%d%d", &r, &t), t--;
while (t--) {
int u, v;
char c;
scanf("%d%d %c\n", &u, &v, &c);
T.push(u, v, c), T.push(v, u, c);
}
T.dfs(r);
} else if (opt == 3) {
scanf("%s", str);
printf("%d\n", T.match_times(str));
} else throw;
}
return 0;
}