您的位置:首页 > 其它

LOJ刷题记录:2006-2011(SCOI2015)

2017-08-10 08:18 232 查看

LOJ刷题记录:2006-2011(SCOI2015)

loj#2006. 「SCOI2015」小凸玩矩阵

水题…

二分答案之后用二分图匹配判断能不能取出k个比当前答案小的。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 505;

struct node {
int to, next, flow, neg;
} edge[MAXN*MAXN];
int head[MAXN], top = 0;
inline void push(int i, int j, int f)
{
++top, edge[top] = (node) {j, head[i], f, top+1}, head[i] = top;
++top, edge[top] = (node) {i, head[j], 0, top-1}, head[j] = top;
}

const int S = MAXN-1, T = S-1;
int lev[MAXN], vis[MAXN], bfstime = 0;
int cur[MAXN];
queue<int> que;

bool bfs()
{
vis[S] = ++bfstime, lev[S] = 0, que.push(S);
while (!que.empty()) {
int nd = que.front(); que.pop();
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (vis[to] == bfstime || !edge[i].flow) continue;
vis[to] = bfstime, lev[to] = lev[nd]+1, que.push(to);
}
}
return vis[T] == bfstime;
}

int dfs(int nd, int flow)
{
if (nd == T || !flow) return flow;
int ans = 0, t;
for (int &i = cur[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (lev[to] != lev[nd]+1 || !edge[i].flow) continue;
t = dfs(to, min(flow, edge[i].flow));
ans += t, flow -= t, edge[i].flow -= t, edge[edge[i].neg].flow += t;
if (!flow) break;
}
return ans;
}

int dinic()
{
int ans = 0;
while (bfs()) memcpy(cur, head, sizeof head), ans += dfs(S, INT_MAX);
return ans;
}

int n, m, k;
int a[255][255];

bool judge(int mid)
{
memset(head, 0, sizeof head);
top = 0;
for (int i = 1; i <= n; i++) push(S, i, 1);
for (int i = 1; i <= m; i++) push(i+n, T, 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (a[i][j] <= mid)
push(i, n+j, 1);
return dinic() >= k;
}

int main()
{
scanf("%d%d%d", &n, &m, &k);
k = n-k+1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);
int l = 0, r = 1e9, mid;
while (l <= r) {
mid = (l+r)>>1;
if (judge(mid)) r = mid-1;
else l = mid+1;
}
printf("%d\n", r+1);
return 0;
}


loj#2007. 「SCOI2015」国旗计划

细节神tm多……

考虑贪心,对于每一个战士,我们显然要选取能接上他火炬且右端点尽可能靠后点..这样我们就可以连一条有向边,然后每个战士就要找他的后继中第一个使得覆盖总长超过m的…这显然可以用倍增维护。

这样只要维护last[nd][i]为包含nd在内向后2i个点,nxt[nd][i]表示不包含nd向后2i个点,sum[nd][i]表示包含nd向后2i的覆盖总长。之所以要记录last,nxt两个东西就是要去除中间重复的部分。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005;

struct segment {
int l, r, id;
friend bool operator < (const segment &a, const segment &b)
{ return a.l < b.l; }
} p[MAXN*2];
int n, m;
int nxt[MAXN][20], last[MAXN][20];
long long sum[MAXN][20];
int dat[MAXN];

int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &p[i].l, &p[i].r), p[i].id = i;
if (p[i].l > p[i].r) p[i].r += m;
}
sort(p+1, p+n+1);
for (int i = 1; i <= n; i++) p[i+n] = (segment){p[i].l+m, p[i].r+m, i};
int pt = 1;
for (int i = 1; i <= n; i++) {
while (p[pt+1].l <= p[i].r) pt++;
nxt[i][0] = pt<=n?pt:pt-n;
sum[i][0] = p[i].r-p[i].l+1;
last[i][0] = i;
}
for (int j = 1; j < 20; j++)
for (register int i = 1; i <= n; i++)
nxt[i][j] = nxt[nxt[i][j-1]][j-1], last[i][j] = nxt[last[i][j-1]][j-1];
for (int j = 1; j < 20; j++)
for (register int i = 1; i <= n; i++) {
int R1 = p[last[i][j-1]].r;
int R2 = p[nxt[i][j-1]].l; if (R2 > m) R2 -= m;
if (R1 > m) R1 -= m;
if (R1 < R2) R1 += m;
sum[i][j] = sum[i][j-1]+sum[nxt[i][j-1]][j-1]-(R1-R2+1);
}
for (int i = 1; i <= n; i++) {
long long cur = 0;
int pt = i, ans = 0;
int last_pos = p[i].l-1;
for (int j = 19; j >= 0; j--) {
int pre = pt;
if (sum[pt][j]+cur-(last_pos-p[pt].l+1) <= m) {
cur += sum[pt][j]-(last_pos-p[pt].l+1), last_pos = p[last[pt][j]].r, pt = nxt[pt][j], ans |= 1<<j;
if (last_pos > m) last_pos -= m;
}
}
dat[p[i].id] = ans+1;
}
for (int i = 1; i <= n; i++) {
printf("%d ", dat[i]);
}
puts("");
return 0;
}


loj#2008. 「SCOI2015」小凸想跑步

半平面交还是非常好建的…算是第一个成功的板子吧..

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005;

const double eps = 1e-8;

struct point {
double x, y;
double ang;
friend point operator + (const point &a, const point &b)
{ return (point){a.x+b.x, a.y+b.y}; }
friend point operator - (const point &a, const point &b)
{ return (point){a.x-b.x, a.y-b.y}; }
friend double operator * (const point &a, const point &b)
{ return a.x*b.y-a.y*b.x; }
friend point operator * (double lambda, const point &a)
{ return (point){lambda*a.x, lambda*a.y};  }
void display()
{ printf("(%.2f, %.2f) ", x, y); }
};

typedef point vec;

struct half_plane {
point pt;
vec v;
friend bool operator < (const half_plane &a, const half_plane &b)
{ return a.v.ang < b.v.ang; }
inline bool inside(point &p)
{ return v*(p-pt) >= eps; }
friend point operator * (const half_plane &a, const half_plane &b)
{
double lambda = (a.pt*b.v-b.pt*b.v)/(b.v*a.v);
return a.pt+lambda*a.v;
}
void display()
{ pt.display(); printf("+ k"); v.display(), printf("ang = %.2f\n", v.ang);}
};

half_plane p[MAXN*2];
point q[MAXN*2];
int l, r;

double HPC(vector<half_plane> &v)
{
sort(v.begin(), v.end());
l = 1, r = 0;
for (auto i : v) {
while (l < r && !i.inside(q[r])) r--;
while (l < r && !i.inside(q[l+1])) l++;
if (l > r || abs(i.v.ang-p[r].v.ang) >= eps) p[++r] = i, q[r] = i*p[r-1];
}
while (l < r && !p[l].inside(q[r])) r--;
q[r+1] = p[r]*p[l];
q[r+2] = q[l+1];
double ans = 0;
if (r-l+1 >= 3) for (int i = l+1; i <= r+1; i++) ans += q[i]*q[i+1];
return ans/2;
}

int n;
point pts[MAXN];
vector<half_plane> v;

int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%lf%lf", &pts[i].x, &pts[i].y);
double sig = 0;
for (int i = 0; i < n; i++) sig += pts[i]*pts[(i+1)%n];
sig /= 2;
for (int i = 0; i < n; i++) {
vec ps = pts[(i+1)%n]-pts[i]; ps.ang = atan2(ps.y, ps.x);
v.push_back((half_plane){pts[i], ps});
}
for (int i = 1; i < n; i++) {
point p1 = pts[i], p2 = pts[(i+1)%n], pt;
double a = p1.y-p2.y-pts[0].y+pts[1].y, b = p2.x-p1.x-pts[1].x+pts[0].x;
double c = p1*p2-pts[0]*pts[1];
if (abs(b) <= eps) {
double xx = -c/a;
pt = (point) {xx, 0};
} else {
double xx = -c/b;
pt = (point) {0, xx};
}
v.push_back((half_plane){pt, (vec){b, -a, atan2(-a, b)}});
}
double ans = HPC(v);
printf("%.4f\n", ans/sig);
return 0;
}


loj#2009. 「SCOI2015」小凸玩密室

好神啊…

用ui,j表示走完i的子树后走到i的深度为j的祖先的兄弟的最小代价;

用vi,j表示走完i的子树后走到i的深度为j的祖先的最小代价,用u算出v。

枚举起点,计算答案。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005;
int n;
long long a[MAXN], b[MAXN];
inline int lc(int nd)
{ return nd<<1; }
inline int rc(int nd)
{ return nd<<1|1; }
inline int fa(int nd)
{ return nd>>1; }
inline long long L(int nd)
{ return b[(nd<<1)-1]; }
inline long long R(int nd)
{ return b[nd<<1]; }
long long f[MAXN][20], g[MAXN][20];
int depth[MAXN];
long long dis[MAXN];

int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = 1; i < n; i++) scanf("%lld", &b[i]);
depth[1] = 1, dis[1] = 0;
for (int i = 2; i <= n; i++) depth[i] = depth[fa(i)]+1, dis[i] = dis[fa(i)]+b[i-1];
for (int i = n; i >= 1; i--)
for (int j = depth[i], z = i; j > 1; j--, z = fa(z)) {
int bro = (z&1)?z-1:z+1;
if (lc(i)>n && rc(i)>n) f[i][j] = a[bro]*(dis[i]+dis[bro]-2*dis[fa(z)]);
else if (rc(i)>n) f[i][j] = a[lc(i)]*L(i)+f[lc(i)][j];
else f[i][j] = min(a[lc(i)]*L(i)+f[lc(i)][depth[i]+1]+f[rc(i)][j], a[rc(i)]*R(i)+f[rc(i)][depth[i]+1]+f[lc(i)][j]);
}
for (int i = n; i >= 1; i--)
for (int j = depth[i], z = i; j >= 0; j--, z = fa(z)) {
if (lc(i)>n && rc(i)>n) g[i][j] = (dis[i]-dis[z])*a[z];
else if (rc(i)>n) g[i][j] = a[lc(i)]*L(i)+g[lc(i)][j];
else g[i][j] = min(a[lc(i)]*L(i)+f[lc(i)][depth[i]+1]+g[rc(i)][j], a[rc(i)]*R(i)+f[rc(i)][depth[i]+1]+g[lc(i)][j]);
}
long long ans = g[1][0];
//cerr << ans << endl;
for (int i = 2; i <= n; i++) {
long long cnt = g[i][depth[i]-1];
int nd = i, p = fa(nd), bro = (nd&1)?nd-1:nd+1;
while (p) {
if (rc(p)<=n) cnt += b[bro-1]*a[bro]+g[bro][depth[p]-1];
else cnt += b[p-1]*a[fa(p)];
nd = p, p = fa(nd), bro = (nd&1)?nd-1:nd+1;
}
//cerr << cnt << endl;
ans = min(ans, cnt);
}
printf("%lld\n", ans);
return 0;
}


loj2010有点毒…先空着

loj#2011. 「SCOI2015」情报传递

离线处理,树上可持久化线段树即可…然而并跑不过两个log的树剖…

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005;

int l[MAXN*20], r[MAXN*20], lc[MAXN*20], rc[MAXN*20], sum[MAXN*20], tp = 0, root[MAXN];

void build(int &nd, int opl, int opr)
{
nd = ++tp, l[nd] = opl, r[nd] = opr;
if (opl < opr) build(lc[nd], opl, (opl+opr)>>1), build(rc[nd], ((opl+opr)>>1)+1, opr);
}

void modify(int pre, int &nd, int pos)
{
nd = ++tp, l[nd] = l[pre], r[nd] = r[pre], sum[nd] = sum[pre]+1;
if (l[nd] < r[nd]) {
int mid = (l[nd]+r[nd])>>1;
if (pos <= mid) rc[nd] = rc[pre], modify(lc[pre], lc[nd], pos);
else lc[nd] = lc[pre], modify(rc[pre], rc[nd], pos);
}
}

int query(int nd, int opl, int opr)
{
if (opl > opr) return 0;
if (l[nd] == opl && r[nd] == opr) return sum[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 query(lc[nd], opl, mid)+query(rc[nd], mid+1, opr);
}
}

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 n, f[MAXN], rt, q;
int a[MAXN], b[MAXN], c[MAXN], d[MAXN];
int T[MAXN], ans[MAXN];
int fa[MAXN], vis[MAXN], depth[MAXN];
int findf(int nd)
{ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }

vector<pair<int, int> > qy[MAXN];

void tarjan(int nd)
{
vis[nd] = 1, depth[nd] = depth[f[nd]]+1;
for (vector<pair<int, int> >::iterator i = qy[nd].begin(); i != qy[nd].end(); ++i)
if (vis[i->first]) ans[i->second] = findf(i->first);
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to != f[nd]) tarjan(to);
}
fa[findf(nd)] = findf(f[nd]);
}

void dfs_build(int nd)
{
if (T[nd]) modify(root[f[nd]], root[nd], T[nd]);
else root[nd] = root[f[nd]];
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to != f[nd]) dfs_build(to);
}
}

int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &f[i]);
if (f[i]) push(f[i], i);
else rt = i;
}
scanf("%d", &q);
for (int i = 1; i <= q; i++) {
scanf("%d", &a[i]);
if (a[i] == 1) scanf("%d%d%d", &b[i], &c[i], &d[i]), qy[b[i]].push_back(make_pair(c[i], i)), qy[c[i]].push_back(make_pair(b[i], i));
else scanf("%d", &b[i]), T[b[i]] = i;
}
tarjan(rt);
build(root[0], 1, n);
dfs_build(rt);
for (int i = 1; i <= q; i++) {
if (a[i] == 2) continue;
int lca = ans[i], min_pos = max(1, i-d[i]);
int sig = query(root[b[i]], 1, min_pos-1)+query(root[c[i]], 1, min_pos-1)-query(root[lca], 1, min_pos-1)-query(root[f[lca]], 1, min_pos-1);
printf("%d %d\n", depth[b[i]]+depth[c[i]]-depth[lca]*2+1, sig);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: