LOJ刷题记录:2012-2017(SCOI2016)
loj#2012. 「SCOI2016」背单词
神贪心…
所有串翻转,考虑建一棵这样的树, i→j 当且仅当 si 是 sj 最长的前缀。这棵树可以用trie建出来。第一种方案显然不会采纳,因此每一个元素放完后,才能放他的子树,而且要按照从子树大小由小到大的放…
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 600005;
int chl[MAXN][26], top = 0, root = 0;
int fin[MAXN];
void push(int &nd, const char *str)
{
if (!nd) nd = ++top;
if (*str == '\0') fin[nd] = 1;
else push(chl[nd][*str-'a'], str+1);
}
int n;
char str[MAXN];
vector<int> v[MAXN];
int siz[MAXN];
int dfn[MAXN], dfn_top = 0;
long long ans = 0;
void dfs_build(int nd, int last)
{
if (last && fin[nd]) v[last].push_back(nd), last = nd;
if (!last) last = nd;
for (int i = 0; i < 26; i++)
if (chl[nd][i])
dfs_build(chl[nd][i], last);
}
void dfs_siz(int nd)
{
siz[nd] = 1;
for (auto i : v[nd]) {
dfs_siz(i);
siz[nd] += siz[i];
}
}
bool cmp(int i, int j)
{ return siz[i] < siz[j]; }
void calc(int nd, int f)
{
dfn[nd] = ++dfn_top;
if (nd != root) ans += dfn[nd]-dfn[f];
sort(v[nd].begin(), v[nd].end(), cmp);
for (auto i : v[nd])
calc(i, nd);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%s", str);
int L = strlen(str);
reverse(str, str+L);
push(root, str);
}
fin[root] = 1;
dfs_build(root, 0);
dfs_siz(root);
calc(root, 0);
printf("%lld\n", ans);
return 0;
}
loj#2013. 「SCOI2016」幸运数字
小清新数据结构~
原来是用的树剖-线性基三个 log 不科学做法,现在又写了一个点分治-线性基两个 log ,果然跑的比谁都快。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 20005, MAXQ = 200005;
struct linear_base {
long long k[70];
int top = 0;
inline void clear()
{ top = 0; }
void push(long long x)
{
for (int i = 1; i <= top; i++)
if ((x^k[i]) < x)
x ^= k[i];
if (!x) return;
for (int i = 1; i <= top; i++)
if ((x^k[i]) < k[i])
k[i] ^= x;
k[++top] = x;
for (int i = top; i > 1 && k[i] > k[i-1]; i--)
swap(k[i], k[i-1]);
}
long long get_max()
{
long long ans = 0;
for (int i = 1; i <= top; i++)
if ((ans^k[i])>ans)
ans ^= k[i];
return ans;
}
friend linear_base operator + (const linear_base &a, long long b)
{
linear_base c = a;
c.push(b);
return c;
}
friend linear_base operator + (const linear_base &a, const linear_base &b)
{
linear_base c = a;
for (int i = 1; i <= b.top; i++)
c.push(b.k[i]);
return c;
}
};
struct node {
int to, next;
} edge[MAXN*2];
int head[MAXN], top = 0;
void push(int i, int j)
{ edge[++top] = (node) {j, head[i]}, head[i] = top; }
int vis[MAXN], siz[MAXN], col[MAXN];
long long ans[MAXN*10];
long long d[MAXN];
linear_base lb[MAXN];
int n, q;
void dfs_siz(int nd, int f)
{
siz[nd] = 1;
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f || vis[to]) continue;
dfs_siz(to, nd), siz[nd] += siz[to];
}
}
void dfs_find_center(int nd, int f, const int totsiz, int &ans, int &max_siz)
{
int cnt = (f!=0)*(totsiz-siz[nd]);
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f || vis[to]) continue;
dfs_find_center(to, nd, totsiz, ans, max_siz);
cnt = max(cnt, siz[to]);
}
if (cnt < max_siz) ans = nd, max_siz = cnt;
}
void dfs_paint(int nd, int f, int c)
{
col[nd] = c;
lb[nd] = lb[f]+d[nd];
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f || vis[to]) continue;
dfs_paint(to, nd, c);
}
}
vector<pair<int, int> > qy[MAXN];
int tp = 0;
int tmp = 0;
void dfs_calc(int nd, int f)
{
for (auto i : qy[nd])
if (col[i.first] > tmp && col[i.first] < col[nd])
ans[i.second] = (lb[i.first]+lb[nd]).get_max();
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f || vis[to]) continue;
dfs_calc(to, nd);
}
}
void calc(int nd)
{
dfs_siz(nd, 0);
int center = nd, tt = INT_MAX;
dfs_find_center(nd, 0, siz[nd], center, tt), vis[center] = 1;
tmp = tp;
lb[center].clear(), lb[center].push(d[center]);
// cerr << center << endl;
for (int i = head[center]; i; i = edge[i].next) {
int to = edge[i].to;
if (vis[to]) continue;
dfs_paint(to, center, ++tp);
}
for (auto i : qy[center])
if (col[i.first] > tmp) {
ans[i.second] = lb[i.first].get_max();
}
for (int i = head[center]; i; i = edge[i].next) {
int to = edge[i].to;
dfs_calc(to, center);
}
for (int i = head[center]; i; i = edge[i].next) {
int to = edge[i].to;
if (!vis[to]) calc(to);
}
}
int main()
{
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) scanf("%lld", &d[i]);
for (int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
push(u, v), push(v, u);
}
for (int i = 1; i <= q; i++) {
int x, y; scanf("%d%d", &x, &y);
if (x == y) ans[i] = d[x];
else qy[x].push_back(make_pair(y, i)), qy[y].push_back(make_pair(x, i));
}
calc(1);
for (int i = 1; i <= q; i++)
printf("%lld\n", ans[i]);
return 0;
}
loj#2014. 「SCOI2016」萌萌哒
真-神数据结构系列…..
和花神游历各国是一类题…本质有用的操作只有很少(这道题是 O(n) 的),但有很多的无效操作,关键在于如何将无效操作最小化。
这个题的方法是你用ST表的思路,将
l,r
拆成二的整次幂,用
f[n][i]
为
n
开始
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, q;
pair<int,int> f[MAXN][20];
pair<int,int> findf(const pair<int,int> &pr)
{ return f[pr.first][pr.second]!=pr?f[pr.first][pr.second]=findf(f[pr.first][pr.second]):pr; }
const int mod = 1e9+7;
void link(const pair<int,int> &a, const pair<int,int> &b)
{
// cerr << a.first << " " << a.second << "," << b.first << " " << b.second << endl;
pair<int,int> fa = findf(a), fb = findf(b);
if (fa == fb) return;
f[fa.first][fa.second] = fb;
if (a.second != 0) {
link(make_pair(a.first, a.second-1), make_pair(b.first, b.second-1));
link(make_pair(a.first+(1<<(a.second-1)), a.second-1), make_pair(b.first+(1<<(b.second-1)), b.second-1));
}
}
int main()
{
scanf("%d%d", &n, &q);
int l1, l2, r1, r2;
for (int i = 1; i <= n; i++)
for (int j = 0; j < 20; j++)
f[i][j] = make_pair(i, j);
for (int i = 1; i <= q; i++) {
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
int d = r1-l1+1;
for (register int j = 0; j < 20; j++)
if (d&(1<<j)) {
link(make_pair(l1, j), make_pair(l2, j));
l1 += (1<<j), l2 += (1<<j);
}
}
int ans = 1;
for (int i = 1; i <= n; i++)
if (f[i][0] == make_pair(i, 0)) {
if (findf(make_pair(1, 0)) == f[i][0]) ans = (long long)ans*9%mod;
else ans = (long long)ans*10%mod;
}
printf("%d\n", ans);
return 0;
}
loj#2015. 「SCOI2016」妖怪
一眼题…设 a/b=λ ,先二分个答案,然后发现每一个妖怪 λ 可行取值都在一个区间内,就做了。
但是这样会被卡时间…然后发现有几个数据都是递增的,可能到最后才能发现冲突…random_shullfe了一下就过了…以后要养成random_shuffle的好习惯…
大家貌似是三分的…?按照codeforces那个题的经验三分可能有点问题吧..
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int n;
struct p {
double a, b;
friend bool operator < (const p &a, const p &b)
{ return a.a<b.a||(a.a==b.a&&a.b<b.b); }
} pt[MAXN];
double L = 0, R = 1e10;
bool judge(double k)
{
L = 0, R = 1e10;
register double w, dt;
for (register int i = 1; i <= n; i++) {
w = k-pt[i].a-pt[i].b, dt = sqrt(w*w-4*pt[i].a*pt[i].b);
L = max((w-dt)/(2*pt[i].b), L), R = min((w+dt)/(2*pt[i].b), R);
if (L >= R) return 0;
}
return 1;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%lf%lf", &pt[i].a, &pt[i].b);
//sort(pt+1, pt+n+1);
srand(time(0));
random_shuffle(pt+1, pt+n+1);
double l = 0, r = 1.5e8, mid;
for (int i = 1; i <= n; i++)
l = max(l, pt[i].a+pt[i].b+2*sqrt(pt[i].a*pt[i].b));
while (r-l >= 1e-6) {
mid = (l+r)/2;
if (judge(mid)) r = mid;
else l = mid;
}
printf("%.4f\n", l);
return 0;
}
loj#2016. 「SCOI2016」美味
新套路get√…
其实那个异或只不过是改变了每一位0/1的优先级而已..所以按照新的优先级二分(其实就是逐位确定)然后用主席树统计一下答案…
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int n, m;
int a[MAXN];
int sum[MAXN*40], l[MAXN*40], r[MAXN*40], lc[MAXN*40], rc[MAXN*40], top = 0, root[MAXN];
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 pre, int &nd, int pos, int dt)
{
nd = ++top, l[nd] = l[pre], r[nd] = r[pre], sum[nd] = sum[pre]+dt;
if (l[nd] < r[nd]) {
int mid = (l[nd]+r[nd])/2;
if (pos <= mid) rc[nd] = rc[pre], modify(lc[pre], lc[nd], pos, dt);
else lc[nd] = lc[pre], modify(rc[pre], rc[nd], pos, dt);
}
}
int query(int nd, int opl, int opr)
{
// cerr << opl << " " << opr << " " << l[nd] << " " << r[nd] << endl;
if (opl > opr) return 0;
if (l[nd] == opl && r[nd] == opr) return sum[nd];
else {
int mid = (l[nd]+r[nd])/2;
if (opr <= mid) return query(lc[nd], opl, opr);
else if (opl > mid) return query(rc[nd], opl, opr);
else return query(lc[nd], opl, mid)+query(rc[nd], mid+1, opr);
}
}
int query_sum(int L, int R, int opl, int opr)
{
// cerr << opl << " " << opr << endl;
return query(root[R], opl, opr)-query(root[L-1], opl, opr);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
build(root[0], 1, n);
for (int i = 1; i <= n; i++) modify(root[i-1], root[i], a[i], 1);
cerr << query_sum(1, 3, 2, 4) << endl;
for (int i = 1; i <= m; i++) {
int b, x, opl, opr, cur = 0;
scanf("%d%d%d%d", &b, &x, &opl, &opr);
for (int j = 20; j >= 0; j--) {
int pos = ((b&(1<<j))==0);
if (query_sum(opl, opr, max(1, (cur|(pos<<j))-x), min((cur|(pos<<j)|((1<<j)-1))-x, n)) > 0) {
cur |= pos<<j;
} else cur |= (!pos)<<j;
}
printf("%d\n", cur^b);
}
return 0;
}
loj#2017. 「SCOI2016」围棋
轮廓线dp….坑待填