2022NOIP A层联测34 bs 串 英语作文 计算器 愤怒的小鸟

T1[图论:并查集维护寻找特殊环]给出一个无向图,点权是0或者1,你可以从任意起点出发,每到达一个点,把这个点的权值放到你构造的字符串的末尾,并且这个点的权值取反。给出K次操作,添加一条\(u--v\)的边,输出每次操作后是否存在一种遍历方式使得可以无限构造字符串\('sbsbsbsbs'或者'bsbsbsbsbsb'\)。(n<=2e5,K<=2e5)

考场

一看到无限肯定是要找环,不然总有走尽的时候。然后考虑是长什么样的环:模拟一下发现就是奇环,而且相邻边是01,(只有一个地方不是),奇数是为了保证最后没有地方走了刚好接上。开始考虑应该怎么找环,拿出来不现实,太多了,突然想到可以枚举边用并查集维护已经是01相邻的点集合,因为01具有传递性。然后直接判断每对相邻点是11或者00的是否在同一并查集内。\(O(nlogn)\)

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define chu printf
#define ll long long
#define ull unsigned long long
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 1e5 + 100;
int n, m, q, fa[N], rfa[N];
char s[N];
vector<int> e[N];
struct operation {
    int x, y;
} op[N];
int del[N];
inline int father(int x) { return (x == fa[x]) ? x : (fa[x] = father(fa[x])); }
inline bool check(int cut) {
    // chu("check:%d\n",cut);
    for (rint i = 1; i <= n; ++i) fa[i] = rfa[i], del[i] = 0;
    for (rint i = 1; i <= cut; ++i) {
        if (s[op[i].x] != s[op[i].y]) {
            fa[father(op[i].x)] = father(op[i].y);
        } else
            e[op[i].x].push_back(op[i].y), del[op[i].x]++;  //只加入一对就可以
    }
    bool yes = 0;
    for (rint i = 1; i <= n; ++i) {
        int x = father(i);
        for (rint to : e[i]) {
            if (father(to) == x) {
                // chu("%d and %d can merge\n",to,i);
                yes = 1;
                break;
            }
        }
        if (yes)
            break;
    }
    for (rint i = 1; i <= n; ++i) {
        while (del[i]) e[i].pop_back(), del[i]--;
    }
    // chu("the rel:%d\n",yes);
    return yes;
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    freopen("bssb.in", "r", stdin);
    freopen("bssb.out", "w", stdout);
    n = re(), m = re(), q = re();
    scanf("%s", s + 1);
    for (rint i = 1; i <= n; ++i) fa[i] = i;
    for (rint i = 1; i <= m; ++i) {
        int x = re(), y = re();
        if (s[x] != s[y]) {
            fa[father(x)] = father(y);
        } else
            e[x].push_back(y);  // chu("push back:%d %d\n",x,y);
    }
    for (rint i = 1; i <= n; ++i) rfa[i] = fa[i];
    for (rint i = 1; i <= q; ++i) op[i].x = re(), op[i].y = re();
    if (check(0)) {
        // chu("in\n");
        for (rint i = 1; i <= q; ++i) chu("Yes\n");
        return 0;
    }
    int l = 1, r = q, ans = q + 1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid))
            ans = mid, r = mid - 1;
        else
            l = mid + 1;
    }
    for (rint i = 1; i < ans; ++i) chu("No\n");
    for (rint i = ans; i <= q; ++i) chu("Yes\n");
    return 0;
}
/*
3 0 3
sbb
1 2
1 3
2 3

5 3 2
sbsbs
1 2
2 3
3 4
1 5
4 5
*/

T2[计数DP]定义句子是由主语谓语宾语构成,文章由句子构成,句子的主语和宾语成分都可以是句子。给出n个单词,标明词性可以是什么,求合法文章方案数。(n<=5e5)

考场

认为合法句子是形似\(abababababcbcbcbcbc..\)结构,但是总是算不对

正解

漏情况啦!句子可以是\(ab*b*b*b*b*bc\)的结构,因为句子和句子也可以接在一起,然后的DP就很显然了。

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define chu printf
#define ll long long
#define ull unsigned long long
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 5e5 + 100, mod = 998244353;
int f[N][3], n;
char s[N][3];
inline void upd(int& x, int y) {
    x = x + y;
    x = (x >= mod) ? (x - mod) : x;
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    freopen("paper.in", "r", stdin);
    freopen("paper.out", "w", stdout);
    n = re();
    for (rint i = 1; i <= n; ++i) scanf("%s", s[i]);
    for (rint i = 1; i <= n; ++i) {
        if (s[i][0] == 'T')
            upd(f[i][0], (f[i - 1][2] + f[i - 1][1] + 1) % mod);
        if (s[i][1] == 'T')
            upd(f[i][1], (f[i - 1][0] + f[i - 1][2]) % mod);
        if (s[i][2] == 'T')
            upd(f[i][2], f[i - 1][1]);
        upd(f[i][0], f[i - 1][0]);
        upd(f[i][1], f[i - 1][1]);
        upd(f[i][2], f[i - 1][2]);
    }
    chu("%d", f[n][2]);
    return 0;
}
/*
f[i][a/b/c]
f[i][a]=f[i-1][c]+f[i-1][b]+1
f[i][b]=f[i-1][a]+f[i-1][c]
f[i][c]=f[i-1][b]

*/

T3[数据结构:线段树/问题转化]给出一个操作序列a,1代表\(x/2\),2代表\(x*2\),3代表\(x*2+1\),4,5,6分别代表把这些操作无限重复直到不合法为止(要求数字的值域是\([1,2^n-1]\))。给出多次询问表示求顺次重复\([l,r]\)序列的操作对x进行,最后x的结果。(n,m<=2e5)

考场

暴力,本来想优化从最后一个4开始,但是懒,后来听说这么干有76分?

正解

就是很奇妙!把操作转化成二叉树上的跳父亲和左儿子右儿子,发现限制只和x的深度有关(也必须和深度有关),于是线段树上维护\((a,b,c)\)表示把操作转化成\((x/(2^a)*(2^b)+c)\)的形式进行区间合并,需要维护m个不同长度,只在初始化时考虑赋值就可以(只和长度有关嘛!)

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define chu printf
#define ll long long
#define ull unsigned long long
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 2e5 + 100;
int n, m, q, p[N];
char s[N];
struct node {
    int a, b, c;
    inline int solve(int x) { return ((x >> a) << b) | c; }
} t[N << 2][31];
inline node merge(node x, node y) {
    int a = x.a, b = x.b, c = x.c, d = y.a, e = y.b, f = y.c;
    if (b > d) {
        return (node){ a, e + b - d, ((c >> d) << e) + f };
    } else {
        return (node){ a + d - b, e, ((c >> d) << e) + f };
    }
}
inline void get(int k, int r)  // k是指针,合并子树信息,是pushup干的事,我是要初始化
{
    if (r == 1) {
        t[k][0] = (node){ 0, 0, 0 };
        for (rint i = 1; i < m; ++i) t[k][i] = (node){ 1, 0, 0 };
    } else if (r == 2) {
        t[k][m - 1] = (node){ 0, 0, 0 };
        for (rint i = 0; i < m - 1; ++i) t[k][i] = (node){ 0, 1, 0 };
    } else if (r == 3) {
        t[k][m - 1] = (node){ 0, 0, 0 };
        for (rint i = 0; i < m - 1; ++i) t[k][i] = (node){ 0, 1, 1 };
    } else if (r == 4) {
        for (rint i = 0; i < m; ++i) t[k][i] = (node){ i, 0, 0 };
    } else if (r == 5) {
        for (rint i = 0; i < m; ++i) t[k][i] = (node){ 0, m - 1 - i, 0 };
    } else {
        for (rint i = 0; i < m; ++i) t[k][i] = (node){ 0, m - 1 - i, (1 << (m - 1 - i)) - 1 };
    }
}
#define lson (rt << 1)
#define rson (rt << 1 | 1)
inline void pushup(int rt) {
    for (rint i = 0; i < m; ++i) {
        node x = t[lson][i];
        int len = i - x.a + x.b;
        t[rt][i] = merge(t[lson][i], t[rson][len]);
    }
}
inline void build(int rt, int l, int r) {
    if (l == r) {
        get(rt, p[l]);
        return;
    }
    int mid = (l + r) >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    pushup(rt);
}
inline void change(int rt, int l, int r, int pos, int vl) {
    if (l == r) {
        get(rt, vl);
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid)
        change(lson, l, mid, pos, vl);
    else
        change(rson, mid + 1, r, pos, vl);
    pushup(rt);
}
inline node query(int rt, int l, int r, int L, int R, int key) {
    if (L <= l && r <= R)
        return t[rt][key];
    int mid = (l + r) >> 1;
    if (R <= mid)
        return query(lson, l, mid, L, R, key);
    if (L > mid)
        return query(rson, mid + 1, r, L, R, key);
    node ans1 = query(lson, l, mid, L, R, key);
    node ans2 = query(rson, mid + 1, r, L, R, key - ans1.a + ans1.b);
    return merge(ans1, ans2);
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    freopen("calculate.in", "r", stdin);
    freopen("calculate.out", "w", stdout);
    n = re(), m = re(), q = re();
    scanf("%s", s + 1);
    for (rint i = 1; i <= n; ++i) p[i] = s[i] - '0';
    build(1, 1, n);
    for (rint i = 1; i <= q; ++i) {
        int opt = re();
        if (opt == 1) {
            int l = re(), r = re(), x = re();
            chu("%d\n", query(1, 1, n, l, r, log2(x)).solve(x));
        } else {
            int x = re(), y = re();
            change(1, 1, n, x, y);
        }
    }
    return 0;
}
/*
 */

T4[可反悔贪心]玩游戏有n个关卡,每个关卡1星花费a元,2星b元,3星c元,求最终或者1星及以上p个,2星及以上q个,3星r个最少花费。(n<=2e5)

考场

可反悔贪心不会写,直接DP还挂了,为什么挂了呢?因为理解错题目含义了,你完全可以全部选r,没有必要刚好选qpr的个数,而且dp我忘记继承前面的最小值了,导致他只能计算连续物品组合。

20pts
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define chu printf
#define ll long long
#define ull unsigned long long
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 2e5 + 100;
int n, p, q, r, a[N], b[N], c[N];
ll f[55][55][55][55];
bool vis[N];
int P[N];
int p2,q2,r2;
inline void deal_baoli() {
    memset(f, 0x3f, sizeof(f));
    f[0][0][0][0] = 0;  //
    ll rel = 1e18;
	//for(rint i=1;i<=n;++i)b[i]=min(b[i],c[i]),a[i]=min(a[i],b[i]);
    for (rint i = 1; i <= n; ++i) {
        for (rint j = 0; j <= i; ++j) {
            for (rint k = 0; k <= i; ++k) {
                for (rint l = 0; l <= i; ++l) {
                    if (j)
                        f[i][j][k][l] = min(f[i][j][k][l], f[i - 1][j - 1][k][l] + a[i]);
                    if (k)
                        f[i][j][k][l] = min(f[i][j][k][l], f[i - 1][j][k - 1][l] + b[i]);
                    if (l)
                        f[i][j][k][l] = min(f[i][j][k][l], f[i - 1][j][k][l - 1] + c[i]);
						f[i][j][k][l]=min(f[i][j][k][l],f[i-1][j][k][l]);
                }
            }
        }
		for(rint j=r2;j<=n;++j)
		{
			for(rint k=max(0,q2-j);k<=n;++k)
			{
				for(rint l=max(0,p2-k-j);l<=n;++l)
				rel=min(rel,f[i][l][k][j]);
			}
		}
    }
    chu("%lld", rel);
}
int main() {
    //freopen("1.in", "r", stdin);
    //freopen("angrybird.out", "w", stdout);
    freopen("angrybird.in","r",stdin);
    freopen("angrybird.out","w",stdout);
    n = re(), p = re(), q = re(), r = re();
	p2=p,q2=q,r2=r;
    for (rint i = 1; i <= n; ++i) a[i] = re();
    for (rint i = 1; i <= n; ++i) b[i] = re();
    for (rint i = 1; i <= n; ++i) c[i] = re();
    int q1 = q-r, p1 = p-max(q,r);
    p = p1;
    q = q1;
    if (!p && !q && !r) {
        chu("0");
        return 0;
    }
    if (p && !q && !r) {
        ll sun = 0;
        for (rint i = 1; i <= n; ++i) a[i] = min({ a[i], b[i], c[i] });
        sort(a + 1, a + 1 + n);
        for (rint i = 1; i <= p; ++i) sun += a[i];
        chu("%lld", sun);
        return 0;
    }
    if (n <= 50) {
		//chu("df\n");
        deal_baoli();
        return 0;
    }
    ll rel = 0;
    for (rint i = 1; i <= n; ++i) P[i] = i;
    sort(P + 1, P + 1 + n, [&](int x, int y) { return a[x] < a[y]; });
    for (rint i = 1; i <= n; ++i)
        if (p > 0)
            rel += a[P[i]], vis[P[i]] = 1, p--;
    for (rint i = 1; i <= n; ++i) P[i] = i;
    sort(P + 1, P + 1 + n, [&](int x, int y) { return b[x] < b[y]; });
    for (rint i = 1; i <= n; ++i) {
        if (!vis[i] && q) {
            rel += b[P[i]], vis[P[i]] = 1;
            q--;
        }
    }
    for (rint i = 1; i <= n; ++i) P[i] = i;
    sort(P + 1, P + 1 + n, [&](int x, int y) { return c[x] < c[y]; });
    for (rint i = 1; i <= n; ++i) {
        if (!vis[i] && r) {
            rel += c[P[i]], vis[P[i]] = 1;
            r--;
        }
    }
    chu("%lld", rel);
    return 0;
}
/*
3 2 1 1
1 2 5
10 6 6
2 8 8
*/

正解

考虑对问题进行一下简单转化,令\(q=q-r,p=p-max(q,r),bi=min(bi,ci),ci=min(ci,bi)\),首先对于pqr的是从“至少”变成“至多”,为什么?因为考虑在贪心情况下如果2星选择大于q-r个,那么首先3星必须选r个,从p里面扔掉一些肯定合法而且会花费变小。后面的是在干嘛?考虑可反悔贪心进行的是要求严格选择这些价值,但是和题目要求是不符合的,因为1星选择了k个,完全可以等价成3星(如果3星反而更便宜,那就完全没有必要选1星了),所以先传递一下一定不劣的选择。

然后就是维护7个堆表示选a反悔成b,选a反悔成c,选b反悔成a,选b反悔成c,选c,选b,选a。(不用反悔c,可能就是用费用流模型解释的东西吧,已经是在最优决策下选择的a和b,所以任意的c再去变成ab的一定不会更优)

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define chu printf
#define ll long long
#define ull unsigned long long
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 2e5 + 100;
int n, p, q, r, a[N], b[N], c[N];
int vis[N];
priority_queue<pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > > qbc, qab, qac, qba, qa, qb,
    qc;
ll ans;
inline void make_1star(int x) {
    vis[x] = 1;
    qab.push(make_pair(b[x] - a[x], x));
    qac.push(make_pair(c[x] - a[x], x));
}
inline void make_2star(int x) {
    vis[x] = 2;
    qba.push(make_pair(a[x] - b[x], x));
    qbc.push(make_pair(c[x] - b[x], x));
}
inline void make_3star(int x) { vis[x] = 3; }
inline ll getc_fromb() {
    while (!qbc.empty() && vis[qbc.top().second] != 2) qbc.pop();
    return qbc.empty() ? 1e18 : qbc.top().first;
}
inline ll getc_froma() {
    while (!qac.empty() && vis[qac.top().second] != 1) qac.pop();
    return qac.empty() ? 1e18 : qac.top().first;
}
inline ll getb_froma() {
    while (!qab.empty() && vis[qab.top().second] != 1) qab.pop();
    return qab.empty() ? 1e18 : qab.top().first;
}
inline ll geta_fromb() {
    while (!qba.empty() && vis[qba.top().second] != 2) qba.pop();
    return qba.empty() ? 1e18 : qba.top().first;
}
inline ll getc()  //有标记就不能拿,为啥?好吧,确实
{
    while (!qc.empty() && vis[qc.top().second]) qc.pop();
    return qc.empty() ? 1e18 : qc.top().first;
}
inline ll getb() {
    while (!qb.empty() && vis[qb.top().second]) qb.pop();
    return qb.empty() ? 1e18 : qb.top().first;
}
inline ll geta() {
    while (!qa.empty() && vis[qa.top().second]) qa.pop();
    return qa.empty() ? 1e18 : qa.top().first;
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    freopen("angrybird.in", "r", stdin);
    freopen("angrybird.out", "w", stdout);
    n = re(), p = re(), q = re(), r = re();
    int q_ = q - r;
    int p_ = p - max(q, r);
    q = q_;
    p = p_;
    for (rint i = 1; i <= n; ++i) a[i] = re();
    for (rint i = 1; i <= n; ++i) b[i] = re();
    for (rint i = 1; i <= n; ++i) c[i] = re();
    for (int i = 1; i <= n; i++) b[i] = min(b[i], c[i]);
    for (int i = 1; i <= n; i++) a[i] = min(a[i], b[i]);
    for (rint i = 1; i <= n; ++i) {
        qa.push(make_pair(a[i], i));
        qb.push(make_pair(b[i], i));
        qc.push(make_pair(c[i], i));
    }
    for (rint i = 1; i <= p; ++i)  //先选择a,就是拿p个小的
    {
        make_1star(qa.top().second);
        ans += qa.top().first;
        qa.pop();
    }
    for (rint i = 1; i <= q; ++i) {
        //考虑可以从b直接拿
        ll x = getb();
        ll y = getb_froma() + geta();
        if (x < y) {
            int tmp = qb.top().second;
            qb.pop();
            make_2star(tmp);
            ans += x;
        } else {
            int tmp = qab.top().second;
            qab.pop();
            make_2star(tmp);
            tmp = qa.top().second;
            qa.pop();
            make_1star(tmp);
            ans += y;
        }
    }
    for (rint i = 1; i <= r; ++i) {
        ll A = getc();
        ll B = getc_fromb() + getb(), C = getc_fromb() + getb_froma() + geta();
        ll D = getc_froma() + geta(), E = getc_froma() + geta_fromb() + getb();
        if (A < B && A < C && A < D && A < E) {
            int tmp = qc.top().second;
            qc.pop();
            make_3star(tmp);
            ans += A;
        } else if (B < C && B < D && B < E) {
            int tmp = qbc.top().second;
            qbc.pop();
            make_3star(tmp);
            tmp = qb.top().second;
            qb.pop();
            make_2star(tmp);
            ans += B;
        } else if (C < D && C < E) {
            int tmp = qbc.top().second;
            qbc.pop();
            make_3star(tmp);
            tmp = qab.top().second;
            qab.pop();
            make_2star(tmp);
            tmp = qa.top().second;
            qa.pop();
            make_1star(tmp);
            ans += C;
        } else if (D < E) {
            int tmp = qac.top().second;
            qac.pop();
            make_3star(tmp);
            tmp = qa.top().second;
            qa.pop();
            make_1star(tmp);
            ans += D;
        } else {
            int tmp = qac.top().second;
            qac.pop();
            make_3star(tmp);
            tmp = qba.top().second;
            qba.pop();
            make_1star(tmp);
            tmp = qb.top().second;
            qb.pop();
            make_2star(tmp);
            ans += E;
        }
    }
    chu("%lld", ans);
    return 0;
}
/*
防止玛n*100行+
函数:
【1】把i选成3星的-->2和1的反悔
标记?
(选的时候如果vis[top]!=说明目前物品不在这,就pop,直到可以)
【2】从7个堆里面取出来最优的答案,返回
10个函数?
what...
*/

posted on 2022-11-24 21:57  HZOI-曹蓉  阅读(14)  评论(0编辑  收藏  举报