首页 > 其他分享 >2023暑假集训

2023暑假集训

时间:2023-07-12 23:16:46浏览次数:44  
标签:return int flow long cost 暑假 2023 集训 dp

20230710

I - Visiting Friend(点双/圆方树)

题意

多次询问两个点之间所有路径可能经过的点数,路径只需要满足起点和终点不重复经过。

\(N,M,Q ≤ 5\times10^5\)

题解

建出圆方树,方点点权设为0,圆点点权设为1。维护一下子树和,讨论两个点的LCA是不是其中一个点两种情况,删去不可能经过的点即可。

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

const int N = 2e6 + 10;
int n, tot, m;

vector<int> g[N], G[N], dcc[N];
int w[N];

int dfn[N], low[N], idx;
stack<int> s;
void yfs(int u, int fa) {
    dfn[u] = low[u] = ++idx;
    s.push(u);
    int cnt = 0;
    for (auto v : g[u]) {
        if (v == fa) continue;
        if (!dfn[v]) {
            yfs(v, u);
            low[u] = min(low[u], low[v]);
            if (dfn[u] <= low[v]) {
                tot++;
                int x;
                do {
                    x = s.top(); s.pop();
                    G[x].push_back(tot); G[tot].push_back(x);
                } while (x != v);
                G[u].push_back(tot); G[tot].push_back(u);
                w[tot] = 0;
            }
        } else low[u] = min(low[u], dfn[v]);
    }
}

int dep[N];
int sum[N];
int f[N][22];
void dfs(int u, int fa) {
    //cout << u << "\n";
    f[u][0] = fa;
    dep[u] = dep[fa] + 1;
    sum[u] = w[u];
    for (int i = 1; i <= 21; i++) {
        f[u][i] = f[f[u][i - 1]][i - 1];
    }
    for (auto v : G[u]) {
        if (v == fa) continue;
        dfs(v, u);
        sum[u] += sum[v];
    }
}

int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    int k = dep[x] - dep[y];
    for (int i = 0; i <= 21; i++) {
        if (k >> i & 1) x = f[x][i];
    }
    if (x == y) return x;
    for (int i = 21; i >= 0; i--) {
        if (f[x][i] != f[y][i]) {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}

void solve() {
    cin >> n >> m;
    tot = n;
    idx = 0;
    while (!s.empty()) s.pop();
    for (int i = 1; i <= n * 4; i++) {
        dcc[i].clear(); g[i].clear(); G[i].clear();
        dfn[i] = low[i] = dep[i] = sum[i] = 0;
        w[i] = 1;
        for (int j = 0; j <= 21; j++) f[i][j] = 0;
    }
    for (int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        g[u].push_back(v); g[v].push_back(u);
    }

    yfs(1, 0);
    dfs(1, 0);

    int q; cin >> q;
    while (q--) {
        int u, v; cin >> u >> v;
        if (dep[u] > dep[v]) swap(u, v);
        int t = lca(u, v);
        if (t == u) {
            int k = dep[v] - dep[u] - 1;
            int tv = v;
            for (int i = 0; i <= 21; i++) {
                if (k >> i & 1) tv = f[tv][i];
            }
            cout << sum[tv] - sum[v] + 2 << "\n";
        } else {
            cout << n - (sum[u] - 1) - (sum[v] - 1) << "\n";
        }
    }
}
signed main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int T = 1; cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}
/*
1
9 9
1 2 2 3 3 4 4 5 5 6 6 7 7 1
5 8 5 9
9999
8 9
 */

E - Sum Over Zero(树状数组优化dp)

题意

给定一个序列,选择一些不相交子区间,满足每个子区间的和非负,最大化区间的总长度。

题解

设 \(dp_i\) 表示以 \([1,i]\) 的答案,很容易想到 \(O(n^2)\) 的dp:

for (int i = 1; i <= n; i++) {
    dp[i] = dp[i - 1];	//初始化:不选i
    for (int j = 0; j < i; j++) 
        if (sum[i] - sum[j] >= 0) //更新:枚举所有[j+1,i]非负的区间
            dp[i] = max(dp[i], dp[j] + (i - j) ) 
}

考虑优化:移个项,则更新时有

\[dp[i] - i = \max\limits_{sum[j]\le sum[i]}(dp[j] - j) \]

只需要使用树状数组将每次的dp[i] - i插入到下标sum[i]处,维护前缀最大值即可。

sum[i]可能很大,需要离散化 。同时注意一开始dp[0]-0=0,sum[0]=0。需要将离散化后的0插入到树状数组中。

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

const int N = 4e5 + 10, inf = 1e17;
int n, a[N], sum[N], dp[N];

int pos0, val[N];
void work(int arr[]) {
    for (int i = 1; i <= n; i++) val[i] = arr[i];
    sort(val + 1, val + 1 + n);
    int m = unique(val + 1, val + 1 + n) - val - 1;
    for (int i = 1; i <= n; i++) {
        arr[i] = lower_bound(val + 1, val + 1 + m, arr[i]) - val;
    }
    pos0 = lower_bound(val + 1, val + 1 + m, 0) - val;
}

int t[N];
void add(int i, int x) {
    for (; i <= n; i += i & -i) {
        t[i] = max(t[i], x);
    }
}
int ask(int i) {
    int res = -inf;
    for (; i; i -= i & -i) {
        res = max(res, t[i]);
    }
    return res;
}

void solve() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum[i] = sum[i - 1] + a[i];
        t[i] = -inf;
    }
    work(sum);
    add(pos0, 0);
    for (int i = 1; i <= n; i++) {
        dp[i] = max(dp[i - 1], ask(sum[i]) + i);
        add(sum[i], dp[i] - i);
    }
    cout << dp[n] << "\n";
}


signed main() {
    int T = 1; //cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

20230711

C - Necklace(最小表示法)

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

const int N = 1e7 + 10;
char s[N];

int main() {
    scanf("%s", s);
    int n = strlen(s);
    int p1 = 0, p2 = 1, i = 0;
    while (p1 < n && p2 < n && i < n) {
        if (s[(p1 + i) % n] == s[(p2 + i) % n]) {
            i++;
        } else {
            if (s[(p1 + i) % n] > s[(p2 + i) % n]) {
                p1 += i + 1;
            } else {
                p2 += i + 1;
            }
            i = 0;
            if (p1 == p2) p2++;
        }
    }
    for (int i = 0; i < n; i++) putchar(s[(p1 + i) % n]);
    return 0;
}

B - 我承认阁下的字符串匹配很强(kmp+dp)

题意

给定一个字符串S , 包含小写字母和问号,其中每个问号都可以替换成任意一个小写字母,你需要构造一个方案,最大化字符串T在S出现的次数。

题解

//
// Created by blackbird on 2023/7/9.
//
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;

const int N = 2e6 + 10;
int n, m, ne[N], ans;
char s[N], t[N];

void getnext(char t[], int m) {
    for (int i = 2, j = 0; i <= m; i++) {
        while (j && t[j + 1] != t[i]) j = ne[j];
        if (t[j + 1] == t[i]) j++;
        ne[i] = j;
    }
}

int f[N], g[N];
void solve(char s[], int n, char t[], int m) {
    for (int i = 1; i - 1 + m <= n; i++) {
        bool ok = 1;
        for (int j = 1; j <= m; j++) {
            if (s[i - 1 + j] != t[j] && s[i - 1 + j] != '?') {
                ok = 0; break;
            }
        }
        if (ok) {   //T在相同前后缀处重叠
            int j = ne[m];
            while (j) {
                g[i - 1 + m] = max(g[i - 1 + m], g[i - 1 + j] + 1);
                j = ne[j];
            }
            g[i - 1 + m] = max(g[i - 1 + m], f[i - 1] + 1);
        }
        f[i - 1 + m] = max(f[i - 1 + m - 1], g[i - 1 + m]);
    }
}

signed main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> s + 1 >> t + 1;
    n = strlen(s + 1); m = strlen(t + 1);
    getnext(t, m);
    solve(s, n, t, m);
    cout << f[n] << "\n";
    return 0;
}

K - Difficult Constructive Problem(上下界贪心构造/dp)

题意

给定一个长度为 n 的字符串 s ,其中 si ∈ {‘0’, ‘1’, ‘?’}

另外给定一个整数 k,

将字符串中 ‘?’ 换成 ‘0’ 或 ‘1’,使得满足 1 ≤ i < n 且 si != si+1 的下标 i 恰有 k 个。

求字典序最小的一组解。

题解

//
// Created by blackbird on 2023/7/11.
//
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;

const int N = 2e6 + 10, inf = 1e9;
int n, k, a[N];
char s[N];

int mn[N][2], mx[N][2];

string calc() {
    for (int i = 0; i <= 1; i++) {
        if (s[n] == i + '0') mn[n][i] = mx[n][i] = 0;
        else mn[n][i] = inf, mx[n][i] = -inf;
    }
    for (int i = n - 1; i > 0; i--) {
        for (int j = 0; j <= 1; j++) {
            if (s[i] == j + '0' || s[i] == '?') {
                mn[i][j] = min(mn[i + 1][j], mn[i + 1][1 - j] + 1);
                mx[i][j] = max(mx[i + 1][j], mx[i + 1][1 - j] + 1);
            } else mn[i][j] = inf, mx[i][j] = -inf;
        }
    }


    if (k % 2 != mn[1][s[1] - '0'] % 2) {
        //cout << "wrong evenodd\n";
        return "2";
    }


    string res;
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        bool ok = 0;
        for (int j = 0; j <= 1; j++) {
            int t = (i > 1 && j != res[i - 2] - '0' ? 1 : 0);
            if (mn[i][j] <= k - (cnt + t) && k - (cnt + t) <= mx[i][j]) {
                res.push_back(j + '0');
                cnt += t;
                ok = 1; break;
            }
        }
        if (!ok) {
            return "2";
        }
    }
    return res;

}

void solve() {
    cin >> n >> k >> s + 1;
    char s1 = s[1], sn = s[n];
    string ans = "2";
    for (char i = '0'; i <= '1'; i++) if (s1 == '?' || s1 == i) {
        for (char j = '0'; j <= '1'; j++) if (sn == '?' || sn == j) {
            s[1] = i;
            s[n] = j;
            ans = min(ans, calc());
        }
    }
    if (ans == "2") cout << "Impossible\n";
    else cout << ans << "\n";
}

signed main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int T = 1; cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

20230712

M - Window Arrangement(最小费用最大流)

#pragma GCC optimize(3)
#include <bits/stdc++.h>
//#define int long long
using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
struct Edge{
    int from, to, cap, flow, cost;
    Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){}
};

struct MCMF{
    int n, m, inq[N], d[N], p[N], a[N];
    vector<Edge> edges;
    vector<int> G[N];

    void init(int n){
        this->n = n;
        for(int i = 0; i <= n; i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from,int to,int cap,long long cost){
        edges.push_back(Edge(from,to,cap,0,cost));
        edges.push_back(Edge(to,from,0,0,-cost));
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool BellmanFord(int s,int t,int &flow,long long &cost){
        for(int i = 0; i <= n; i++) d[i] = INF;
        memset(inq, 0, sizeof inq);
        d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;

        queue<int>  Q;
        Q.push(s);
        while(!Q.empty()){
            int u = Q.front(); Q.pop();
            inq[u] = 0;
            for(int i = 0; i < G[u].size(); i++){
                Edge& e = edges[G[u][i]];
                if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){
                    d[e.to] = d[u]+e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u],e.cap-e.flow);
                    if(!inq[e.to]) {Q.push(e.to); inq[e.to] = 1;}
                }
            }
        }
        if(d[t]==INF) return false;
        flow += a[t];
        cost += (long long)d[t]*(long long)a[t];
        for(int u = t; u!=s; u = edges[p[u]].from){
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
        }
        return true;
    }
    int MincostMaxflow(int s,int t,long long &cost){
        int flow = 0;
        cost = 0;
        while(BellmanFord(s,t,flow,cost));
        return flow;
    }
} g;

int n, m, O, S, T, a[51][51], w[51][51];

int calc_grid(int x, int y) {
    return (x - 1) * m + y;
}

int calc_window(int x, int y, int dx, int dy) {
    // ‘|’ windows: n*m + 1 ~ n*m + n*(m-1) = n*(2m-1)
    // '-' windows: n*(2m-1) + 1 ~ n*(2m-1) + (n-1)*m
    if (dx == 0) {
        int base = n * m;
        if (dy == -1) y--, dy = 1; //left to right
        int delta = (m - 1) * (x - 1) + y;
        return base + delta;
    } else {
        int base = n * (m * 2 - 1);
        if (dx == -1) x--, dx = 1;//up to down
        int delta = m * (x - 1) + y;
        return base + delta;
    }
}

signed main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> a[i][j];
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> w[i][j];

    g.init(3 * n * m + 5);
    O = 3 * n * m;
    S = 3 * n * m + 1;
    T = 3 * n * m + 2;
    //cout << "?????OST\n";
    g.AddEdge(O, T, 1e9, 0);
    int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1};
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {

            g.AddEdge(S, calc_grid(i, j), w[i][j], 0);

            for(int d = 0; d < 4; d++) {
                int x = i + dx[d], y = j + dy[d];

                if(x < 1 || y < 1 || x > n || y > m) {
                    g.AddEdge(calc_grid(i, j), O, 1, 0);
                    continue;
                }

                g.AddEdge(calc_grid(i, j), calc_window(i, j, dx[d], dy[d]), 1, 0);

                if (dx[d] + dy[d] == 1)  {
                    g.AddEdge(calc_window(i, j, dx[d], dy[d]), T, 1, 0);
                    g.AddEdge(calc_window(i, j, dx[d], dy[d]), T, 1, a[i][j] * a[x][y]);
                }
            }
        }
    }

    long long cost = 0;
    g.MincostMaxflow(S, T, cost);
    cout << cost << "\n";
    return 0;
}

Squirrel Game(阶梯nim游戏)

image-20230712222658183

考虑模k意义下,构成k个倒着的阶梯Nim问题

阶梯nim问题的结论为,奇数位置的异或和决定状态。(0为先手必败)。

#include<cstdio>
#include<algorithm>
#define ll long long
#define maxn 100005
using namespace std;

int m,n,k;
ll ans,a[maxn],del[maxn];

int main()
{
	scanf("%d%d%d",&m,&n,&k);
	for(int i=1;i<=n;++i)
	{
		scanf("%lld",&a[i]);
		a[i]-=i;
		del[i]=a[i]-a[i-1];
	}
//	for(int i=1;i<=min(n,k);++i)
//	{
//		ll ret=0,mx=0;
//		for(int j=i;j<=n;j+=k)
//		{
//			ret+=del[j];
//			mx=max(mx,del[j]);
//		}
//		if(mx<ret)
//		ret++;
//		ans^=ret;
//	}
	for(int i=n;i>max(0,n-k);--i)
	{
		ll ret=0;
		for(int j=i,fl=1;j>=1;j-=k)
		{
			if(fl)
			ret^=del[j];
			fl^=1;
		}
		ans^=ret;
	}
	if(ans)
	printf("Twinkle\n");
	else printf("Nova\n");
	return 0;
}

标签:return,int,flow,long,cost,暑假,2023,集训,dp
From: https://www.cnblogs.com/vv123/p/17549105.html

相关文章

  • 2023.7.12 鲜花
    昨天2023.7.11考的NOIDay1模拟,今天考Day2。个人感觉Day1比Day2可做多了。Day1T1很好做,然后T2今天调出来了,类似DFA建自动机,把所有可能的数处理出来,接着处理出所有状态的后继进行暴力数位DP匹配,可以获得90分高分。注意变量名取得正常一点,不要把乱七八糟......
  • 2023.7.12打卡
    2023.7.12(1)、今天考完科目二了,差一点点没过,然后从市里回来后学了会Java,看了会综艺,果然,恋爱还是看别人谈才有意思,晚上去打了会球,初中的老毛病又犯了,膝盖疼。(2)、明天学Java,记单词,看下《大道至简》,看辩论赛。(3)、做什么事都得认真对待。因为我去市里没带电脑,所以有几天没发博客。......
  • NOI 2023 考前知识点总复习
    NOI2023考前知识点总复习其实就是把熟悉或不熟悉的东西再过一遍,防止考场上出现会了做法却因为忘了算法而写不出来的问题。可能会一句话概括,也可能附上一点代码片段。如果不想复习知识点,只想要一点考前提示,可以直接翻到本文最底部。目录I.数据结构、树上问题II.数论III.......
  • 每周总结2023/7/12
    hadoop组成  HDFS架构 namenode负责处理数据存储位置,Datanode负责存储的具体数据2NN负责辅助namenode ......
  • 20230710刷题
    B.ObsessionwithRobots先假设除了机器人走的路其他的地方都是障碍,然后记录下来可以走的地方用BFS遍历一遍,判断一个机器人有没有bug#include<bits/stdc++.h>#defineyescout<<"YES"<<'\n'#defineno cout<<"NO"<<'\n'usingnamespacest......
  • 2023-07-12:RocketMQ如何做到消息不丢失?
    2023-07-12:RocketMQ如何做到消息不丢失?答案2023-07-12:RocketMQ通过刷盘机制、消息拉取机制和ACK机制等多种方式来确保消息投递的可靠性,防止消息丢失。1.刷盘机制RocketMQ中的消息分为内存消息和磁盘消息,内存消息在Broker内存中进行读写,磁盘消息则保存在磁盘上。RocketMQ支持同......
  • 20230712练习总结
    AGC009DUninity如果构造一棵点分树,答案是\(\logn\),这是答案上界。将问题转化为每次将若干棵Uninity为\(k\)的树连接到一个点上时将点打上\(k+1\)的\(tag\)。看题面有一个很显然的结论就是两个\(tag=k\)的点间的路径上一定有一个\(tag>k\)。考虑记录\(f_u\)表示......
  • 【文章】Markdown(2023-07-12更新)
    Markdown博客食用效果更佳欢迎大家指出错误并联系这个蒟蒻你是第个看到这篇文章的人。更新日志2023-07-1220:02文章完成前言本蒟蒻最近看了\(\operatorname{QOJ}\)中的FAQ,然后发现了一件很神奇的事:\(\operatorname{FAQ}\)中博客部分写了个什么玩意?所以来补充一下。......
  • NOIP 2023 模拟赛 20230712 C 论剑
    首先是伟大的题面然后是数据范围先解决1-4号数据点1.枚举每个gcd的值p,统计一次答案,得到最小值(期望得分20)\[ans=\min_{p=2}^{\maxa}\sum^n_{i=1}\min(a_i\bmodp,p-(a_i\bmodp)(a>p))\]2.我们可以发现p仅在为质数时不会重复,也可以将p换为质数(期望得分40)两种的时间复......
  • 2023烟台7天编程集训笔记3
    次小生成树:第二小的生成树。次小生成树:删掉一条边,再加上一条边,使得差值尽量小,并且要是一个树。次小生成树:如果一条边在最小生成树上,我们就叫他树边,如果不在最小生成树上就叫他非树边。次小生成树:删掉一条树边,加上一条非树边。次小生成树:倍增LCA询问环上最大的值(章鱼图)。从......