省选测试47


又差不多是0分,两天加起来总共8分,我也是没谁了
T1又写出正解,没用c++11编译,于是CE了,T3看不懂题,找了huge,解释错了之后我就40变0分,
T2的n2很好像,可惜实在是没时间想了,于是写了个最裸的暴力,成为两天里唯一的得分点

A 旅游

题目大意 : 要从x到y,每次最多走到后面第z个城市,走每个城市都会付出a的代价,走某些城市会有收益,问到终点的最大收益

  • n2的dp很好想,设f[i]为走到第i个有收益的城市所得的最大收益,转移也就很显然

\[f[i]=\max_{j=0}^{i-1}f[j]+w\left \lceil \frac{pos[i]-pos[j]}{z} \right \rceil \]

  • 想斜率优化,发现这个值关于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;
}
posted @ 2021-03-25 14:48  Shawk  阅读(45)  评论(0编辑  收藏  举报