TJOI2016 && HEOI2016 解题报告
好吧我来写一波题解骗访问量QAQ
题目可以在cogs提交
bzoj4551~4456
D1T1 tree
树剖可做,然而有更简单的做法,10min搞定
维护一个并查集,时光倒流,如果当前点没有标记就把并查集合并到父亲上,查询就是并查集就可以了
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 200010
using namespace std; int n; struct opt{int tp, x;}q[maxn]; struct Edge{
int to, nxt;
}edge[maxn << 1]; int h[maxn], cnt;
void add(int u, int v){
cnt ++;
edge[cnt].to = v;
edge[cnt].nxt = h[u];
h[u] = cnt;
} int mark[maxn]; int par[maxn];
int find(int x){return x == par[x] ? x : par[x] = find(par[x]);} int fa[maxn]; void dfs(int u){
if(mark[u])par[u] = u;
else par[u] = find(fa[u]);
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(v == fa[u])continue;
fa[v] = u;
dfs(v);
}
} int ans[maxn]; int main(){
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
int test, u, v;
scanf("%d%d", &n, &test);
for(int i = 1; i < n; i ++){
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
mark[1] ++;
char cmd[5];
for(int i= 1; i <= test; i ++){
scanf("%s%d", cmd, &q[i].x);
if(cmd[0] == 'C'){
q[i].tp = 1;
mark[q[i].x] ++;
}
else q[i].tp = 0;
} dfs(1); int x;
for(int i = test; i >= 1; i --){
x = q[i].x;
if(q[i].tp){
mark[x] --;
if(mark[x] == 0)
par[x] = find(fa[x]);
}
else ans[i] = find(x);
} for(int i = 1; i <= test; i ++)
if(q[i].tp == 0)
printf("%d\n", ans[i]);
fclose(stdin);
fclose(stdout);
return 0;
}
D1T2 sort
(其实这道题目是BC原题。。)
首先题目的瓶颈在排序
考虑如果枚举a[q]的答案x,那么可以无视其他的变量的值,将序列转化为0,1,2,其中0代表比x小,1代表等于x,2代表大于x,这样答案就可以变成判定q这个位置是不是1。排序操作变得很好搞,是线段树的区间覆盖和查询。
然而是O(n^2logn)的。。
考虑二分答案x。。0代表比x小,1代表大于等于x
排序如上。如果a[q]为0代表答案大于正确答案,继续向下二分,如果为1代表答案小于等于当前答案,继续向上二分。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 100010
using namespace std; int n, m; struct opt{
int tp, l, r;
}q[maxn]; int a[maxn], Q; int t[maxn << 2], lazy[maxn << 2];
int Lc[maxn << 2], Rc[maxn << 2];
#define lc id << 1
#define rc id << 1 | 1 void build(int id, int l, int r){
int mid = l + r >> 1;
Lc[id] = l, Rc[id] = r;
if(l == r)return;
build(lc, l, mid);
build(rc, mid+1, r);
} void pushdown(int id){
if(lazy[id] > 0){
t[lc] = Rc[lc] - Lc[lc] + 1;
t[rc] = Rc[rc] - Lc[rc] + 1;
lazy[lc] = 1;
lazy[rc] = 1;
} else if(lazy[id] < 0){
t[lc] = 0;
t[rc] = 0;
lazy[lc] = -1;
lazy[rc] = -1;
} lazy[id] = 0;
} void update(int id, int L, int R){
if(L > R)return;
if(Lc[id] == L && Rc[id] == R){
lazy[id] = 1;
t[id] = Rc[id] - Lc[id] + 1;
return;
}
pushdown(id);
int mid = Lc[id] + Rc[id] >> 1;
if(R <= mid)update(lc, L, R);
else if(L > mid)update(rc, L, R);
else update(lc, L, mid), update(rc, mid+1, R);
t[id] = t[lc] + t[rc];
} int ask(int id, int L, int R){
if(Lc[id] == L && Rc[id] == R){
int ret = t[id];
t[id] = 0, lazy[id] = -1;
return ret;
}
pushdown(id);
int mid = Lc[id] + Rc[id] >> 1, ret = 0;
if(R <= mid)ret = ask(lc, L, R);
else if(L > mid)ret = ask(rc, L, R);
else ret = ask(lc, L, mid) + ask(rc, mid+1, R);
t[id] = t[lc] + t[rc];
return ret;
} void Modify1(int l, int r){
int nw = ask(1, l, r);
update(1, r - nw + 1, r);
} void Modify2(int l, int r){
int nw = ask(1, l, r);
update(1, l, l + nw - 1);
} int ask(int id, int p){
if(Lc[id] == Rc[id])
return t[id];
pushdown(id);
int mid = Lc[id] + Rc[id] >> 1, ret = 0;
if(p <= mid)ret = ask(lc, p);
else ret = ask(rc, p);
t[id] = t[lc] + t[rc];
return ret;
} bool check(int nw){
lazy[1] = -1;
t[1] = 0;
for(int i = 1; i <= n; i ++)
if(a[i] >= nw)
update(1, i, i); for(int i = 1; i <= m; i ++){
if(q[i].tp == 0)Modify1(q[i].l, q[i].r);
else Modify2(q[i].l, q[i].r);
} return ask(1, Q);
} int main(){
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
for(int i = 1; i <= m; i ++)
scanf("%d%d%d", &q[i].tp, &q[i].l, &q[i].r);
scanf("%d", &Q);
build(1, 1, n);
int l = 1, r = n;
while(l < r){
int mid = l + (r - l + 1) / 2;
if(check(mid))l = mid;
else r = mid - 1;
}
printf("%d\n", l);
return 0;
}
D1T3 seq
感觉10W就是可以看出来用CDQ来搞DP吧。。
用max[i]代表i位置变化最大值,min[i]代表i位置变化最小值
搞出偏序关系,j<=i, max[j] <=a[i], a[j] <= min[i]
然后用CDQ搞就可以了
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio> #define maxn 100010
using namespace std; int n, m; int a[maxn], mn[maxn], mx[maxn]; struct opt{
int p, mx, mn, a, f;
}q[maxn]; bool cmpp(const opt& a, const opt& b){
return a.p < b.p;
} bool cmpmx(const opt& x, const opt& y){
if(x.mx != y.mx)return x.mx < y.mx;
return x.p < y.p;
} bool cmpa(const opt& x, const opt& y){
if(x.a != y.a)return x.a < y.a;
return x.p < y.p;
} namespace BIT{
int tim, n, t[maxn], vis[maxn];
#define lowbit(x) (x & -x)
void update(int pos, int val){
for(int i = pos; i <= n; i += lowbit(i))
if(vis[i] == tim)t[i] = max(t[i], val);
else vis[i] = tim, t[i] = val;
} int ask(int pos){
int ret = 0;
for(int i = pos; i; i -= lowbit(i))
if(vis[i] == tim)
ret = max(ret, t[i]);
return ret;
}
} void solve(int l, int r){
if(l == r)return;
int mid = (l + r) >> 1;
solve(l, mid);
sort(q + l, q + mid + 1, cmpmx);
sort(q + mid + 1, q + r + 1, cmpa);
BIT::tim ++;
int j = l;
for(int i = mid+1; i <= r; i ++){
for(; j <= mid && q[j].mx <= q[i].a; j ++)
BIT::update(q[j].a, q[j].f);
q[i].f = max(q[i].f, BIT::ask(q[i].mn) + 1);
}
sort(q + l, q + r + 1, cmpp);
solve(mid+1, r);
} int main(){
freopen("seq.in", "r", stdin);
freopen("seq.out", "w", stdout);
scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++){
scanf("%d", &a[i]);
mn[i] = mx[i] = a[i];
}
int x, y;
for(int i = 1; i <= m; i ++){
scanf("%d%d", &x, &y);
mn[x] = min(mn[x], y);
mx[x] = max(mx[x], y);
} int Mx = 0;
for(int i = 1; i <= n; i ++){
q[i].p = i;
q[i].mx = mx[i];
q[i].mn = mn[i];
q[i].a = a[i];
q[i].f = 1;
Mx = max(Mx, mx[i]);
}
BIT::n = Mx;
solve(1, n); int ans = 0;
for(int i = 1; i <= n; i ++)
ans = max(ans, q[i].f);
printf("%d\n", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
D2T1 game
每次DAY2第一题都挂是个什么Flag啊。。
此题和ZOJ1654放置机器人同题。。
样例都差不多好嘛QAQ!
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 52
using namespace std; int n, m; char s[maxn][maxn]; int H[maxn][maxn], L[maxn][maxn], size; struct Edge{
int to, nxt;
}edge[maxn * maxn]; int girl[maxn * maxn]; int h[maxn * maxn], cnt, mark[maxn * maxn]; void add(int u, int v){
cnt ++;
edge[cnt].to = v;
edge[cnt].nxt = h[u];
h[u] = cnt;
} int vis[maxn * maxn], tim;
int find(int u){
vis[u] = tim;
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(vis[v] != tim){
vis[v] = tim;
if(girl[v] == 0 || find(girl[v])){
girl[v] = u;
return true;
}
}
}return false;
} int main(){
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++)
scanf("%s", s[i] + 1);
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
if(s[i][j] == '#')continue;
if(H[i][j-1])H[i][j] = H[i][j-1];
else{
H[i][j] = ++ size;
mark[size] = true;
} if(L[i-1][j])L[i][j] = L[i-1][j];
else L[i][j] = ++ size;
}
} for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
if(s[i][j] == '*')
add(H[i][j], L[i][j]);
}
} int ans = 0;
for(int i = 1; i <= size; i ++)
if(mark[i] && !girl[i])
tim ++, ans += find(i);
printf("%d\n", ans);
return 0;
}
D2T2 sum
考虑这个式子的意义。
Bell数代表的意义是将n个物品随意划分成几个非空集合的划分数。
这里多了一个阶乘代表的是这几个集合有序,多了2^j可以直接分配到递推式中。。
其实我也并不能解释清楚啦。。意会一下~
因为只求F(n)那么我们可以设一个G(n)
令答案ans = sigma(G[i]) [i = 1 .. n]
就有G[i]的递推式G[i] = 2 * sigma{C(j, i) * G[i - j]} j = [1 .. i]
将组合数拆开是卷积形式,i的阶乘可以除到G函数下面
然后NTT
这样做是n^2logn的,所以外面套一层CDQ就可以了。
UPD:据说直接上NTT就可以了?求教..
upd:感谢大神们,从分治转化为多项式求逆,需要把f函数移到一边,化成一个卷积的形式,右边是一个函数,那么就有f * g = h
多项式求逆就可以了
upd2:其实这道题目还有stirling反演代入的式子,可以直接化成卷积,具体见Owaski犇的blog
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 500010
using namespace std;
typedef long long ll;
const ll md = 998244353, G = 3;
int n; ll g[maxn], f[maxn], h[maxn]; ll power_mod(ll a, ll b = md - 2){
ll ret = 1;
while(b > 0){
if(b & 1)ret = ret * a % md;
b >>= 1;
a = a * a % md;
}return ret;
} void NTT(ll A[], int n, int type){
for(int i = 0, j = 0; i < n; i ++){
if(i > j)swap(A[i], A[j]);
for(int t = n >> 1; (j ^= t) < t; t >>= 1);
} for(int k = 2; k <= n; k <<= 1){
ll wn = power_mod(G, type > 0 ? (md-1)/k : md-1-(md-1)/k);
for(int i = 0; i < n; i += k){
ll w = 1;
for(int j = 0; j < k >> 1; j ++){
ll T = w * A[i+j+(k>>1)] % md;
A[i+j+(k>>1)] = (A[i+j] - T + md) % md;
A[i+j] = (A[i+j] + T) % md;
w = w * wn % md;
}
}
} if(type < 0){
ll inv = power_mod(n);
for(int i = 0; i < n; i ++)
(A[i] *= inv) %= md;
}
} ll inv[maxn], fac[maxn]; void solve(int l, int r){
if(l == r)return;
int mid = l + r >> 1;
solve(l, mid);
int len = r - l + 1, n;
for(n = 1; n <= len; n <<= 1);
for(int i = 0; i < n; i ++)f[i] = h[i] = 0;
for(int i = 0; i < n; i ++)f[i] = inv[i];
for(int i = l; i <= mid; i ++)h[i - l] = g[i];
NTT(f, n, 1), NTT(h, n, 1);
for(int i = 0; i < n; i ++)
h[i] = f[i] * h[i] % md;
NTT(h, n, -1);
for(int i = mid + 1; i <= r; i ++)
(g[i] += 2 * h[i - l] % md) %= md;
solve(mid + 1, r);
} int main(){
freopen("sum.in", "r", stdin);
freopen("sum.out", "w", stdout);
scanf("%d", &n);
g[0] = inv[0] = fac[0] = 1;
for(int i = 1; i <= n; i ++)
fac[i] = fac[i-1] * i % md;
inv[n] = power_mod(fac[n]);
for(int i = n - 1; i; i --)
inv[i] = inv[i+1] * (i+1) % md; solve(0, n);
ll ans = 0;
for(int i = 0; i <= n; i ++)
(ans += g[i] * fac[i] % md) %= md;
(ans += md) %= md;
printf("%lld\n", ans);
return 0;
}
D2T3 str
标解是后缀数组
我写的是后缀自动机
字符串的每一个子串都是一个后缀的前缀。将原串反建后缀自动机,每一个点的link指向它的最长前缀。
考虑二分答案L,那么可以在后缀自动机上倍增出c~d的前缀节点p
我们需要找到只有[a~b-L+1]的叶子节点是否在p的子树内,这样一定能保证答案的正确性。
然后对parent树的dfs序搞一个主席树就可以了
然后有各种杂技可以加快速度,不过我觉得这样比较好写。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 200010
using namespace std; int n, m;
//--------------------------------------------------//
char str[maxn];
int whe[maxn], anc[maxn][20];
struct Node{
int nxt[26], link, len;
}st[maxn];
int Root, size, last;
void init(){
Root = size = last = 0;
st[Root].len = 0;
st[Root].link = -1;
} void Extend(int c){
int p = last, cur = ++ size;
st[cur].len = st[p].len + 1;
for(; ~p && st[p].nxt[c] == 0; p = st[p].link)
st[p].nxt[c] = cur;
if(p == -1)
st[cur].link = Root;
else{
int q = st[p].nxt[c];
if(st[q].len == st[p].len + 1)
st[cur].link = q;
else{
int clone = ++ size;
st[clone] = st[q];
st[clone].len = st[p].len + 1;
for(; ~p && st[p].nxt[c] == q; p = st[p].link)
st[p].nxt[c] = clone;
st[q].link = st[cur].link = clone;
}
}last = cur;
} //--------------------------------------------------//
int dfs_clock, In[maxn], Out[maxn];
namespace DFS{
struct Edge{int to, nxt;}edge[maxn];
int h[maxn], cnt;
void add(int u, int v){
cnt ++;
edge[cnt].to = v;
edge[cnt].nxt = h[u];
h[u] = cnt;
} void dfs(int u){
In[u] = ++ dfs_clock;
for(int i = h[u]; i; i = edge[i].nxt)
dfs(edge[i].to);
Out[u] = dfs_clock;
}
}
//--------------------------------------------------//
#define M 5000010
int lc[M], rc[M], v[M], root[maxn], Cnt; int Insert(int rt, int l, int r, int p){
int cur = ++ Cnt;
lc[cur] = lc[rt], rc[cur] = rc[rt], v[cur] = v[rt] + 1;
if(l == r)return cur;
int mid = l + r >> 1;
if(p <= mid)lc[cur] = Insert(lc[rt], l, mid, p);
else rc[cur] = Insert(rc[rt], mid+1, r, p);
return cur;
} int ask(int rt, int l, int r, int L, int R){
if(rt == 0)return 0;
if(l == L && R == r)
return v[rt];
int mid = l + r >> 1;
if(R <= mid)return ask(lc[rt], l, mid, L, R);
if(L > mid) return ask(rc[rt], mid+1, r, L, R);
return ask(lc[rt], l, mid, L, mid) + ask(rc[rt], mid+1, r, mid+1, R);
} int Find(int r, int len){
int t = whe[r];
for(int i = 18; i >= 0; i --){
int v = anc[t][i];
if(st[v].len >= len)
t = v;
}return t;
} int a, b, c, d; bool check(int len){
int u = Find(c, len);
int A = ask(root[b - len + 1], 1, dfs_clock, In[u], Out[u]);
int B = ask(root[a - 1], 1, dfs_clock, In[u], Out[u]);
return A > B;
} int main(){
freopen("str.in", "r", stdin);
freopen("str.out", "w", stdout);
init();
scanf("%d%d", &n, &m);
scanf("%s", str + 1);
for(int i = n; i >= 1; i --)
Extend(str[i] - 'a'), whe[i] = last; for(int i = 1; i <= size; i ++)
DFS::add(st[i].link, i);
DFS::dfs(0);
for(int i = 1; i <= size; i ++)
anc[i][0] = st[i].link;
for(int j = 1; 1 << j <= size; j ++)
for(int i = 0; i <= size; i ++)
anc[i][j] = anc[anc[i][j-1]][j-1]; for(int i = 1; i <= n; i ++)
root[i] = Insert(root[i-1], 1, dfs_clock, In[whe[i]]); while(m --){
scanf("%d%d%d%d", &a, &b, &c, &d);
int l = 0, r = min(b - a + 1, d - c + 1);
while(l < r){
int mid = l + (r - l + 1) / 2;
if(check(mid))l = mid;
else r = mid - 1;
}
printf("%d\n", l);
} return 0;
}
TJOI2016 && HEOI2016 解题报告的更多相关文章
- [HEOI2016/TJOI2016]游戏 解题报告
[HEOI2016/TJOI2016]游戏 看起来就是个二分图匹配啊 最大化匹配是在最大化边数,那么一条边就代表选中一个坐标内的点 但是每一行不一定只会有一个匹配 于是把点拆开,按照'#'划分一下就好 ...
- 洛谷 P4091 [HEOI2016/TJOI2016]求和 解题报告
P4091 [HEOI2016/TJOI2016]求和 题目描述 在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心. 现在他想计算这样一个函数的值: \[ f(n)=\sum_{i=0}^n\ ...
- 洛谷 P4093 [HEOI2016/TJOI2016]序列 解题报告
P4093 [HEOI2016/TJOI2016]序列 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一 ...
- 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告
P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...
- [HEOI2016/TJOI2016] 排序 解题报告(二分答案/线段树分裂合并+set)
题目链接: https://www.luogu.org/problemnew/show/P2824 题目描述: 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在 ...
- HEOI2016解题报告
树 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记 ...
- BZOJ 4552: [Tjoi2016&Heoi2016]排序
4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 579 Solved: 322[Sub ...
- CH Round #56 - 国庆节欢乐赛解题报告
最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...
- 二模13day1解题报告
二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...
随机推荐
- python 下载文件 & 防盗链
偶然下载一种类型的资源,发现好多翻页,右键另存什么的,不胜其烦. 决定用python写几句代码搞定.核心代码如下: from urllib import urlretrieve from urllib ...
- SQL Server游标的使用
转: http://www.cnblogs.com/moss_tan_jun/archive/2011/11/26/2263988.html 游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集 ...
- SQL union和union all的区别
Union因为要进行重复值扫描,所以效率低.如果合并没有刻意要删除重复行,那么就使用Union All 两个要联合的SQL语句 字段个数必须一样,而且字段类型要“相容”(一致): 如果我们需要将两个 ...
- tcp ip detatils
tcp ip detatils 8.关于TCP协议,下面哪种说法是错误的()A.TCP关闭连接过程中,两端的socket都会经过TIME_WAIT状态B.对一个Established状态的TCP连接, ...
- spring mvc form表单提交乱码
spring mvc form表单submit直接提交出现乱码.导致乱码一般是服务器端和页面之间编码不一致造成的.根据这一思路可以依次可以有以下方案. 1.jsp页面设置编码 <%@ page ...
- 基于Delphi的三层数据库系统的实现方法
基于Delphi的三层数据库系统的实现方法 1 引言 当前的数据库应用系统中,按其结构划分为两类,一类是两层结构的数据库应系统,另一类是多层结构的数据库应用系统. 两层结构的数据库应用系统包括客 ...
- Android Your content must have a ListView whose id attribute is 'android.R.id.list'错误的解决办法
在Android开发中,ListView有着很重要的地位,使用的场合也非常的多 错误提示:Your content must have a ListView whose id attribute is ...
- php 用封装类的方法操作数据库和批量删除
封装类 <?php class DBDA { public $host="localhost"; //服务器地址 public $uid="root"; ...
- Java Hour 18 来个CURD吧 (三)
有句名言,叫做10000小时成为某一个领域的专家.姑且不辩论这句话是否正确,让我们到达10000小时的时候再回头来看吧. 本文作者Java 现经验约为18 Hour,请各位不吝赐教. 依赖Jar 包管 ...
- Java线程面试题 Top 50
转自:http://www.importnew.com/12773.html 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java ...