cdcqの省选膜你赛
比赛当天因为在杠hnoi2016的大数据结构没有参加,今天补了一下。挺好玩的虽然不看一句话题意的话真的卡读题
2651. 新史「新幻想史 -现代史-」
一句话题意:
有一个长度为n的整数序列,共m个时刻,在每个时刻都有一个操作,如果是询问操作则询问指定时刻一段区间的和,如果是修改操作则使修改指定时刻到当前时刻的所有时刻一段区间全部增加一个数,另一段区间全部减少一个数
令t代表指定时间
询问和修改拆开
显然的偏序关系:
\[id' < id,\ t' \le t,\ x' \le x
\]
\]
CDQ分治,x那一维的前缀加和前缀求和树状数组搞就行了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=2e5+5;
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n, m, a[N], d, l, r, t; ll s[N], ans[N];
char op[3];
struct meow{
int id, t, x, type, d, qid;
bool operator <(const meow &a) const {return t == a.t ? type > a.type : t < a.t;}
void print(int a) {
printf("q %d %d (%d, %d, %d) %d %d\n", a, type, id, t, x, d, qid);
}
} q[N], _q[N];
int tot, Q;
inline void divide(int type) {
if(type == 0) {
l=read(); r=read(); t=read(); Q++;
tot++; q[tot] = (meow){tot, t, l-1, 0, -1, Q};
tot++; q[tot] = (meow){tot, t, r , 0, 1, Q};
ans[Q] += s[r] - s[l-1];
} else {
l=read(); r=read(); d=read();
tot++; q[tot] = (meow){tot, 0, l-1, 1, d, 0};
tot++; q[tot] = (meow){tot, 0, r , 1, -d, 0};
l=read(); r=read(); d=read();
tot++; q[tot] = (meow){tot, 0, l-1, 1, -d, 0};
tot++; q[tot] = (meow){tot, 0, r , 1, d, 0};
t=read();
q[tot].t = q[tot-1].t = q[tot-2].t = q[tot-3].t = t;
}
}
namespace bit {
struct meow {
ll c[N]; int t[N], T;
inline void add(int p, ll d) {
for(; p<=n; p+=p&-p) {
if(t[p] != T) t[p] = T, c[p] = d;
else c[p] += d;
}
}
inline ll sum(int p) {
ll ans=0;
for(; p; p-=p&-p) if(t[p] == T) ans += c[p];
return ans;
}
inline ll sum(int l, int r) {return sum(r) - sum(l-1);}
}c1, c2;
inline void init() {c1.T++; c2.T++;}
inline void add(int l, int r, ll d) { //printf("add [%d, %d] %lld\n", l, r, d);
c1.add(l, d); c1.add(r+1, -d);
c2.add(l, l*d); c2.add(r+1, -(r+1)*d);
}
inline ll sum(int l, int r) { //printf("sum [%d, %d]\n", l, r);
return (r-l+1) * c1.sum(l) + (r+1) * c1.sum(l+1, r) - c2.sum(l+1, r);
}
} using bit::add; using bit::sum;
void cdq(int l, int r) {
if(l == r) return; //printf("\n----------cdq [%d, %d]\n", l, r);
int mid = (l+r)>>1;
cdq(l, mid); cdq(mid+1, r);
int p1=l, p2=mid+1, p=l;
bit::init();
//for(int i=l; i<=r; i++) q[i].print(i);
while(p1<=mid || p2<=r) {
if(p2>r || (p1<=mid && q[p1]<q[p2])) { //printf("p1 %d %d\n", p1, q[p1].id);
if(q[p1].type) add(1, q[p1].x, q[p1].d);
_q[p++] = q[p1++];
} else {
if(!q[p2].type) ans[ q[p2].qid ] += q[p2].d * sum(1, q[p2].x);// printf("QwQ %d %lld\n", q[p2].d, sum(1, q[p2].x));;
_q[p++] = q[p2++];
}
}
for(int i=l; i<=r; i++) q[i] = _q[i];// q[i].print(i);
//printf("----------end [%d, %d]\n\n", l, r);
}
int main() {
//freopen("in", "r", stdin);
freopen("cdcq_a.in", "r", stdin);
freopen("cdcq_a.out", "w", stdout);
n=read(); m=read();
for(int i=1; i<=n; i++) a[i]=read(), s[i] = s[i-1] + a[i];
for(int i=1; i<=m; i++) {
scanf("%s", op);
if(op[0] == 'Q') divide(0);
else divide(1);
}
//for(int i=1; i<=tot; i++) q[i].print(i);
cdq(1, tot);
for(int i=1; i<=Q; i++) printf("%lld\n", ans[i]);
}
2652. 秘术「天文密葬法」
一句话题意:
给个树,第i个点有两个权值ai和bi,现在求一条长度为m的路径,使得Σai/Σbi最小
裸分数规划... 求路径用点分治
然后m=1肯定是单点取最小呀
调试过程出现各种玄学问题
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=2e5+5, INF=1e9;
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n, m, a[N], b[N]; double val[N];
struct edge{int v, ne;} e[N<<1];
int cnt=1, h[N];
inline void ins(int u, int v) {
e[++cnt]=(edge){v, h[u]}; h[u]=cnt;
e[++cnt]=(edge){u, h[v]}; h[v]=cnt;
}
namespace tr {
int size[N], root, vis[N], f[N], all, ans;
void dfsr(int u, int fa) {
size[u]=1; f[u] = 0;
for(int i=h[u];i;i=e[i].ne)
if(!vis[e[i].v] && e[i].v != fa) {
dfsr(e[i].v, u);
size[u] += size[e[i].v];
f[u] = max(f[u], size[e[i].v]);
}
f[u] = max(f[u], all-size[u]);
if(f[u] < f[root]) root = u;
}
double d[N]; int st[N], top;
void dfscal(int u, int fa, int deep, double now, int rt) {
if(d[m-deep+1] + now - val[rt] < 0) ans=1;
for(int i=h[u];i;i=e[i].ne)
if(!vis[e[i].v] && e[i].v != fa) dfscal(e[i].v, u, deep+1, now + val[e[i].v], rt);
}
void dfsadd(int u, int fa, int deep, double now, int rt) {
if(now < d[deep]) d[deep] = now, st[++top] = deep;
for(int i=h[u];i;i=e[i].ne)
if(!vis[e[i].v] && e[i].v != fa) dfsadd(e[i].v, u, deep+1, now + val[e[i].v], rt);
}
bool dfs(int u) { //printf("dfs %d\n", u);
vis[u]=1;
d[1] = val[u];
for(int i=h[u];i;i=e[i].ne) if(!vis[e[i].v]) {
dfscal(e[i].v, u, 2, val[u] + val[e[i].v], u);
dfsadd(e[i].v, u, 2, val[u] + val[e[i].v], u);
}
while(top) d[ st[top--] ] = 1e18;
if(ans) return true;
for(int i=h[u];i;i=e[i].ne)
if(!vis[e[i].v]) {
all = size[e[i].v]; root=0; dfsr(e[i].v, 0);
if(dfs(root)) return true;
}
return false;
}
bool check(double mid) {
for(int i=1; i<=n; i++) val[i] = a[i] - mid*b[i], vis[i] = 0, d[i] = 1e18;
f[0]=INF; ans=0; top=0;
all=n; root=0; dfsr(1, 0);
return dfs(root);
}
}
void solve() {
double l=0, r=2e10;
while(r-l > 1e-4) {
double mid = (l+r)/2.0;
if(tr::check(mid)) r=mid;
else l=mid;
}
if(l > 2e10 - 1e-3) puts("-1");
else printf("%.2lf", l);
}
int main() {
freopen("in", "r", stdin);
//freopen("cdcq_b.in", "r", stdin);
//freopen("cdcq_b.out", "w", stdout);
n=read(); m=read();
for(int i=1; i<=n; i++) a[i]=read();
for(int i=1; i<=n; i++) b[i]=read();
if(m == -1) {
double ans = INF;
for(int i=1; i<=n; i++) ans = min(ans, (double) a[i] / b[i]);
printf("%.2lf\n", ans);
return 0;
}
for(int i=1; i<n; i++) ins(read(), read());
//printf("%d\n", tr::check(0.45));
solve();
}
2653. 源符「厌川的翡翠」
一句话题意:
给个仙人掌,仙人掌上每个点都能填入[1,t]中的整数,第i个点填j会获得收益v[i][j],求一个最小的c,使得存在一种填数的方案,满足没有两个用边相邻的点填的数差值超过c且所有点的收益和不超过w
二分c,摇身一变成为切糕
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1e5+5, M=1e6+5, INF=1e9;
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n, m, w, k, val[155][155], s, t;
struct meow{int u, v;} a[205];
namespace mf {
struct edge{int v, ne, c, f;} e[M];
int cnt=1, h[N];
inline void ins(int u, int v, int c) {
e[++cnt]=(edge){v, h[u], c, 0}; h[u]=cnt;
e[++cnt]=(edge){u, h[v], 0, 0}; h[v]=cnt;
}
int q[N], head, tail, d[N], vis[N];
bool bfs() {
memset(vis, 0, sizeof(vis));
head=tail=1;
q[tail++]=s; vis[s]=1; d[s]=0;
while(head != tail) {
int u = q[head++];
for(int i=h[u];i;i=e[i].ne)
if(!vis[e[i].v] && e[i].c > e[i].f) {
vis[e[i].v] = 1; d[e[i].v] = d[u]+1;
q[tail++] = e[i].v;
if(e[i].v == t) return true;
}
}
return false;
}
int cur[N];
int dfs(int u, int a) {
if(u==t || a==0) return a;
int flow=0, f;
for(int &i=cur[u];i;i=e[i].ne)
if(d[e[i].v] == d[u]+1 && (f = dfs(e[i].v, min(a, e[i].c-e[i].f)) ) > 0) {
flow += f;
e[i].f += f;
e[i^1].f -= f;
a -= f;
if(a == 0) break;
}
if(a) d[u] = -1;
return flow;
}
int dinic() {
int flow=0;
while(bfs()) {
for(int i=s; i<=t; i++) cur[i] = h[i];
flow += dfs(s, INF);
}
return flow;
}
bool check(int d) {
s = 0; t = n*k + 1;
cnt=1; memset(h, 0, sizeof(h));
for(int i=1; i<=n; i++) {
ins(s, i, INF), ins(i + (k-1)*n, t, val[i][k]);
for(int j=1; j<k; j++) ins(i + (j-1)*n, i + j*n, val[i][j]);
}
for(int i=1; i<=m; i++)
for(int j=1+d; j<=k; j++) {
ins(a[i].u + (j-1)*n, a[i].v + (j-d-1)*n, INF);
ins(a[i].v + (j-1)*n, a[i].u + (j-d-1)*n, INF);
}
return dinic() <= w;
}
}
void solve() {
int l=0, r=k, ans=-1;
while(l <= r) {
int mid = (l+r)>>1;
if(mf::check(mid)) ans = mid, r = mid-1;
else l = mid+1;
}
printf("%d", ans);
}
int main() {
//freopen("in", "r", stdin);
freopen("cdcq_c.in", "r", stdin);
freopen("cdcq_c.out", "w", stdout);
n=read(); m=read(); w=read(); k=read();
for(int i=1; i<=m; i++) a[i].u = read(), a[i].v = read();
for(int i=1; i<=n; i++) for(int j=1; j<=k; j++) val[i][j] = read();
solve();
}