省选测试47
又差不多是0分,两天加起来总共8分,我也是没谁了
T1又写出正解,没用c++11编译,于是CE了,T3看不懂题,找了huge,解释错了之后我就40变0分,
T2的n2很好像,可惜实在是没时间想了,于是写了个最裸的暴力,成为两天里唯一的得分点
A 旅游
题目大意 : 要从x到y,每次最多走到后面第z个城市,走每个城市都会付出a的代价,走某些城市会有收益,问到终点的最大收益
- n2的dp很好想,设f[i]为走到第i个有收益的城市所得的最大收益,转移也就很显然
-
想斜率优化,发现这个值关于pos[i]的函数是每z个都是平的,然后降a,然后z个不变,然后降a这样,不是很能斜率优化
-
于是我把他们都表示在1到z里,然后只取有用的(就是最上面的),set维护,查询的时候在set里二分,写起来很鬼畜
-
正解思路和我我差不多,只是他是用线段树维护,常数较大
Code
Show Code
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; (c < '0' || c > '9'); c = getchar()) if (c == '-') f = -1;
for (;!(c < '0' || c > '9'); c = getchar()) x = x * 10 + c - '0';
return x * f;
}
int s, t, k, m, n, a[N], w[N];
set< pair<int, ll> > st;
set< pair<int, ll> > ::iterator it1, it2;
ll f[N], ans;
ll Cal(int x) {
if (!x) return 0;
int a = (x - 1) / k, b = x - a * k;
return (st.lower_bound(make_pair(b, -1e18)) -> second) - 1ll * a * m;
}
void Insert(int x, ll w) {
if (!x) x = k, w -= m;
int a = (x - 1) / k, b = x - a * k;
pair<int, ll> tmp = make_pair(b, w + 1ll * a * m);
st.insert(tmp);
it1 = st.lower_bound(tmp);
it2 = ++it1; --it1;
if (it2 != st.end()) {
if ((it2 -> second) > (it1 -> second)) return st.erase(it1), void();
}
while (it1 != st.begin()) {
it2 = --it1; ++it1;
if ((it2 -> second) < (it1 -> second)) st.erase(it2);
else break;
}
}
int main() {
freopen("tourist.in", "r", stdin);
freopen("tourist.out", "w", stdout);
s = read(); t = read() - s; k = read();
m = read(); n = read(); Insert(k, -m);
for (int i = 1; i <= n; ++i) {
int p = read() - s, w = read();
ll tmp = Cal(p) + w;
Insert(p, tmp); Insert(((p - 1) / k + 1) * k, tmp - m);
}
printf("%lld\n", Cal(t));
return 0;
}
B 宝石 (Unaccepted)
题目大意 : n颗宝石,可以有D种颜色,问有多少种方案可以满足找到至少m组颜色相同的宝石,不能重复选
- 生成函数
Code
Show Code
C 线段
题目大意 : 有n条线段,第i条连接点i和i+1,动态修改线段断不断,询问两点共有多长的时间的联通的
-
把询问的y减1,可以转换成求x到y线段状态全是1的时刻有多少个,将每个询问x,y对应到二维平面上的一个点
-
考虑改变第x个点的状态会对哪些询问造成影响
-
设l,r为从x向左右延伸,全是1的极大区间的左右端点,发现左端点在[l,x],右端点在[x,r]内的点都会被影响
-
转换到二维平面上就是左下角为(l,x),右上角为(x,r)的区间会受到影响
-
所以,当时间为i的修改使x断开,就将受影响的区间都加上一个i,使x链接的话就给区间减去i
-
对于时间为i个询问(x,y+1),设(x,y)点的值为ans,如果x,y之间全是1,就是ans+i,因为在连接的时候减去了个j,所以要+i,如果不全是1的话就直接是ans
-
现在问题就转换成了在二维平面上区间加,单点查,可以kdtree,cdq,二维线段树,不过树状数组套动态开点线段树好写,跑得的也快
-
写树状数组套动态开点线段树的话,就得变差分了,
-
要给一个左下角为(l,x),右上角为(x,r)的区间加上w,就给(l,x)+w,(x+1,r+1)+w,(x+1,x)-w,(l,r+1)-w
-
单点查询就要查差分数组的前缀和就好了
Code
Show Code
#include <set>
#include <cstdio>
#define ls t[rt].l
#define rs t[rt].r
using namespace std;
const int N = 3e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
char c[N];
set<int> s;
set<int> ::iterator it;
int n, m, rt[N], trc;
struct Tree {
int s, l, r;
}t[N*72];
void Add(int &rt, int l, int r, int x, int w) {
if (!rt) rt = ++trc;
t[rt].s += w;
if (l == r) return;
int mid = l + r >> 1;
if (x <= mid) Add(ls, l, mid, x, w);
else Add(rs, mid + 1, r, x, w);
}
void Add(int x, int y, int w) {
for (; x <= n; x += x & -x)
Add(rt[x], 1, n, y, w);
}
int Ask(int rt, int l, int r, int x) {
if (r <= x) return t[rt].s;
int mid = l + r >> 1, ans = 0;
if (ls) ans = Ask(ls, l, mid, x);
if (rs && x > mid) ans += Ask(rs, mid+1, r, x);
return ans;
}
int Ask(int x, int y, int ans = 0) {
for (; x; x -= x & -x)
ans += Ask(rt[x], 1, n, y);
return ans;
}
int main() {
freopen("segment.in", "r", stdin);
freopen("segment.out", "w", stdout);
n = read(), m = read();
scanf("%s", c + 1);
s.insert(0); s.insert(++n);
for (int i = 1; i <= n; ++i) {
c[i] -= '0';
if (!c[i]) s.insert(i);
}
for (int i = 1; i <= m; ++i) {
char od; scanf(" %c", &od);
int x = read();
if (od == 't') {
int l = x, r = x;
it = s.lower_bound(x);
if (*it == x) ++it; r = *it-1;
if (*(--it) == x) --it; l = *it+1;
if (c[x] ^= 1) {
s.erase(x);
Add(l, x, -i), Add(x + 1, x, i);
Add(l, r + 1, i), Add(x + 1, r + 1, -i);
}
else {
s.insert(x);
Add(l, x, i), Add(x + 1, x, -i);
Add(l, r + 1, -i), Add(x + 1, r + 1, i);
}
}
else {
int y = read() - 1, ans = Ask(x, y);
it = s.lower_bound(x);
if (it != s.end() && *it <= y) printf("%d\n", ans);
else printf("%d\n", ans + i);
}
}
return 0;
}