湖南多校5

J 签到
A 模拟

Problem - M - Codeforces
做过加强版:AT_abc262_h [ABC262Ex] Max Limited Sequence - 洛谷

直接沿用 \(dp_{i,j}\) 表示考虑完前 \(i\) 个位置,上一个取到的位置为 \(j\) 且满足右端点 \(r_x\le i\) 的区间的最大代价。设 \(pos_i\) 表示 \(\max_{r_x\le i} l_x\)

转移时考虑 \(i\neq j\),则 \(dp_{i,j}=dp_{i-1,j}\)
\(i=j\),则 \(dp_{i,i}=\min_{pos_{i-1}\le k<i}dp_{i-1,k}+c_i\)

线段树维护即可。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 5;
int n, m;
int c[N];
struct node{ int l, r; } a[N], b[N];
int pos[N];

bool cmp(node &a, node &b)
{
    return (a.l == b.l ) ? (a.r < b.r ) : a.l < b.l ;
}

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

ll dp[N << 2], lazy[N << 2];

void pushup(int k){ dp[k] = min(dp[ls(k)], dp[rs(k)]); }

void pushdown(int k)
{
    if(lazy[k] == inf) return ;
    dp[ls(k)] = dp[rs(k)] = lazy[k];
    lazy[ls(k)] = lazy[rs(k)] = lazy[k];
    lazy[k] = inf;
}

void build(int k, int l, int r)
{
    dp[k] = lazy[k] = inf;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(ls(k), l, mid), build(rs(k), mid + 1, r);
}

void update(int k, int l, int r, int L, int R, ll val)
{
    if(L <= l && r <= R)
    {
        dp[k] = lazy[k] = val;
        return ;
    }
    pushdown(k);
    int mid = (l + r) >> 1;
    if(L <= mid) update(ls(k), l, mid, L, R, val);
    if(R > mid) update(rs(k), mid + 1, r, L, R, val);
    pushup(k);
}

ll query(int k, int l, int r, int L, int R)
{
    if(L <= l && r <= R) return dp[k];
    pushdown(k);
    int mid = (l + r) >> 1;
    if(R <= mid) return query(ls(k), l, mid, L, R);
    if(L > mid) return query(rs(k), mid + 1, r, L, R);
    return min(query(ls(k), l, mid, L, R), query(rs(k), mid + 1, r, L, R));
}

void get()
{
    build(1, 0, n);
    update(1, 0, n, 0, 0, 0);
    for(int i = 1; i <= n; ++i)
    {
        ll x = query(1, 0, n, pos[i - 1], i - 1);
        if(pos[i - 1] < pos[i]) update(1, 0, n, pos[i - 1], pos[i] - 1, inf - 1);
        update(1, 0, n, i, i, x + c[i]);
    }
    printf("%lld\n", query(1, 0, n, 0, n));
}

void solve()
{  
    n = read(), m = read();
    for(int i = 1; i <= n; ++i) c[i] = read();
    for(int i = 1; i <= m; ++i)
    {
        a[i].l = read(), a[i].r = read();
        pos[a[i].r ] = max(pos[a[i].r ], a[i].l );
    }
    sort(a + 1, a + m + 1, cmp);
    int tot = 1;
    b[1] = a[1];
    for(int i = 2; i <= n; ++i)
    {
        if(a[i].l == b[tot].l ) continue;
        b[++tot] = a[i];
    }
    for(int i = 1; i <= n; ++i) pos[i] = max(pos[i], pos[i - 1]);
    get();
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - G - Codeforces
分类讨论

只需要关注最后一位。
思考后发现对于一开始就能向下取整的直接向下取整最优,此时剩下若干个 \(3\)\(4\),分类讨论得到最优策略为先按照 \(3+4\) 匹配,之后按照 \(3+3\) 或者 \(4+4+4\) 匹配,不能匹配的刷卡支付。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int n;
int cnt[N];

void solve()
{  
    ll sum = 0;
    n = read();
    for(int i = 1; i <= n; ++i)
    {
        double x;
        scanf(" %lf", &x);
        ll y = (ll)(x * 100 + 0.5);
        ll z = y % 5;
        if(z <= 2) sum += y - z;
        else
        {
            ++cnt[z];
            sum += y - z;
        }
    }
    int t = min(cnt[3], cnt[4]);
    sum += t * 5;
    cnt[3] -= t, cnt[4] -= t;
    if(cnt[3])
    {
        int xx = cnt[3] / 2;
        sum += xx * 5;
        cnt[3] -= xx * 2;
        sum += cnt[3] * 3;
    }
    if(cnt[4])
    {
        int xx = cnt[4] / 3;
        sum += xx * 10;
        cnt[4] -= xx * 3;
        sum += 4 * cnt[4];
    }
    printf("%d.%02d\n", sum / 100, sum % 100);
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - I - Codeforces

重要性质(认真读题读出来的)一张通用票从第一次开始使用后,会涵盖后面的连续的旅行计划,即不会出现一张票在第 \(i\) 次旅行使用,第 \(i+2\) 次旅行使用,第 \(i+1\) 次旅行不使用的情况。

\(dp_i\) 表示完成前 \(i\) 次旅行计划所需的最小话费,枚举接下来买哪一张通用票或者单独买下一场的票,转移是一个区间取 \(min\),线段树维护。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e4 + 5, M = 105;
int n, K;
int t[N], f[N];
int p[M], d[M], c[M];

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

ll dp[N << 2], lazy[N << 2];

void pushup(int k)
{
    dp[k] = min(dp[ls(k)], dp[rs(k)]);
}

void pushdown(int k)
{
    if(lazy[k] == inf) return ;
    dp[ls(k)] = min(dp[ls(k)], lazy[k]);
    dp[rs(k)] = min(dp[rs(k)], lazy[k]);
    lazy[ls(k)] = min(lazy[ls(k)], lazy[k]);
    lazy[rs(k)] = min(lazy[rs(k)], lazy[k]);
    lazy[k] = inf;
}

void build(int k, int l, int r)
{
    dp[k] = lazy[k] = inf;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(ls(k), l, mid), build(rs(k), mid + 1, r);
}

void update(int k, int l, int r, int L, int R, ll val)
{
    if(L <= l && r <= R)
    {
        dp[k] = min(dp[k], val);
        lazy[k] = min(lazy[k], val);
        return ;
    }
    pushdown(k);
    int mid = (l + r) >> 1;
    if(L <= mid) update(ls(k), l, mid, L, R, val);
    if(R > mid) update(rs(k), mid + 1, r, L, R, val);
    pushup(k);
}

ll query(int k, int l, int r, int L, int R)
{
    if(L <= l && r <= R) return dp[k];
    pushdown(k);
    int mid = (l + r) >> 1;
    if(R <= mid) return query(ls(k), l, mid, L, R);
    if(L > mid) return query(rs(k), mid + 1, r, L, R);
    return min(query(ls(k), l, mid, L, R), query(rs(k), mid + 1, r, L, R));
}

void solve()
{  
    n = read(), K = read();
    for(int i = 1; i <= n; ++i)
    {
        t[i] = read(), f[i] = read();
    }
    for(int i = 1; i <= K; ++i)
    {
        p[i] = read(), d[i] = read(), c[i] = read();
    }
    build(1, 0, n);
    update(1, 0, n, 0, 0, 0);
    for(int i = 0; i < n; ++i)
    {
        ll x = query(1, 0, n, i, i);
        update(1, 0, n, i + 1, i + 1, x + f[i + 1]);
        for(int k = 1; k <= K; ++k)
        {
            int l = i + 1, r = n;
            while(l < r)
            {
                int mid = (l + r + 1) >> 1;
                if(mid - i > d[k] || t[mid] - t[i + 1] + 1 > p[k]) r = mid - 1;
                else l = mid;
            }
            update(1, 0, n, i + 1, l, x + c[k]);
        }
    }
    printf("%lld\n", query(1, 0, n, n, n));
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - E - Codeforces
数学 二分

由于 \(O_i\) 升序排序,一定选择 \(O_1\)\(O_n\),再选择一个最接近 \(x=\frac{O_1+O_n}{2}\) 的数,二分查找第一个大于 \(x\) 的位置 \(l\),从 \(l-1\)\(l\) 中取最优。

#include<bits/stdc++.h>
using namespace std;
int query(int l)
{
	int x;
	cout<<"? "<<l<<endl;
	cin>>x;
	return x;
}
#define ld long double
int main()
{
	int n;
	cin>>n;
	int a1=query(1);
	int an=query(n);
	int val=(a1+an)/2;
	int l=2,r=n-1;
	while(r>l)
	{
		int mid=(l+r)/2;
		int k=query(mid);
		if(k>val)
		{
			r=mid;
		}
		else
		{
			l=mid+1;
		}
	}
	if(l==2)
	{
		cout<<"! "<<1<<' '<<l<<' '<<n<<endl;
	}
	else
	{
		int k=query(l);
		long double ans1=sqrt(an-k)+sqrt(k-a1);
		int k2=query(l-1);
		long double ans2=sqrt(an-k2)+sqrt(k2-a1);
		if(ans1>ans2)
		{
			cout<<"! "<<1<<' '<<l<<' '<<n<<endl;
		}
		else
		{
			cout<<"! "<<1<<' '<<l-1<<' '<<n<<endl;
		}
	}
}

Problem - K - Codeforces
暴力

对于每一种子串,记录所有的endpos,从这些endpos中取一些位置,相邻两个位置差大于等于这个子串的长度,贪心取位置即可。

如何求endpos,枚举所有子串,将它的哈希值映射到一个编号,也不必将所有endpos记录下来,按照先 \(len\) 再左端点 \(l\) 的方式遍历子串,能保证对于每一种子串,得到的endpos是单调递增的,直接开始贪心,记录上一次这个子串的endpos的位置。

学到了:vector在建立时占用6个int的空间,本题开 \(n^2\) 个vector会直接爆空间。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const ull base = 13331;
const int N = 5e3 + 5;
char s[N];
ull H[N], B[N];

unordered_map< ull, int > mp[N];
int tot;
int cnt[N * N], last[N * N], L[N * N];

void solve()
{
    scanf(" %s", s + 1);
    int n = strlen(s + 1);
    B[0] = 1;
    for(int i = 1; i <= n; ++i) H[i] = H[i - 1] * base + s[i], B[i] = B[i - 1] * base;
    for(int len = 1; len <= n; ++len)
        for(int l = 1, r = len; r <= n; ++l, ++r)
        {
            ull t = H[r] - H[l - 1] * B[len];
            int id = 0;
            if(mp[len].find(t) != mp[len].end()) id = mp[len][t];
            else id = mp[len][t] = ++tot;
            L[id] = len;
            if(last[id] == 0 || last[id] + len <= r) ++cnt[id], last[id] = r;
        }
    int ans = n + 1;
    for(int i = 1; i <= tot; ++i)
    {
        ans = min(ans, n - cnt[i] * L[i] + cnt[i] + L[i]);
    }
    printf("%d\n", ans);
}   

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - F - Codeforces
强联通分量

对图求强联通分量后,发现如果一个强联通分量中的一个步骤失败,整个强联通分量的步骤都会失败,所以成功当且仅当所有点都成功。缩点后得到 \(DAG\),选择入度为 \(0\) 的点一定最优。

学到了:cout会自动控制精度,会将0.1+1e-7判定为0.1,可能导致将精确值视为精度误差舍去,因此以后再输出浮点数时,不论用cout还是printf都要限制小数位数。本题使用printf("%.250Lf")通过。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=1e9+7;
#define M(A,B) make_pair(A,B)
#define ld long double
const int N = 2e5 + 5;
int n, m;
ld a[N], p[N];
vector<int>E[N],scc[N];
int dfn[N], low[N], dfncnt, s[N], in_stack[N], tp, in[N];
int sc, col[N], siz[N];
void tarjan(int u) {
  low[u] = dfn[u] = ++dfncnt, s[++tp] = u, in_stack[u] = 1;
  for (int i :E[u]) {
    int v = i;
    if (!dfn[v]) {
      tarjan(v);
      low[u] = min(low[u], low[v]);
    } else if (in_stack[v]) {
      low[u] = min(low[u], dfn[v]);
    }
  }
  if (dfn[u] == low[u]) {
    ++sc;
    do {
      scc[sc].push_back(s[tp]);
      in_stack[s[tp]] = 0;
      col[s[tp]] = sc;
    } while (s[tp--] != u);
  }
}
int main(){
	cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= m; i++) {
        int a, b; cin >> a >> b;
        E[b].push_back(a);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) tarjan(i);
    }
    for (int i = 1; i <= n; i++) {
        for (int j : E[i]) {
            if (col[i] == col[j]) continue;
            in[col[j]]++;
        }
    }
    ld ans = 0;
    for (int i = 1; i <= sc; i++) {
        if (!in[i]) {
            p[i] = 1;
            for (int j : scc[i]) {
                p[i] = p[i] * ((ld)(1.0) - a[j]);
            }
            ans = max(ans, p[i]);
        }
    }
    printf("%.250Lf\n", ans);

}

Problem - C - Codeforces

猜结论,按照能力值从大到小考虑,每个人都选择当前对自己最优的比赛,我不会证。

怎么跟高考志愿一样,分数高的人先选,分数低的人不影响他的决策,如果一个分数高的想换志愿,分数比他低的人也想换志愿,所以从大到小贪心就对(?)

#include<bits/stdc++.h>
using namespace std;
#define int long long
pair<int,int>p[200005];
int n,m;
int val[200005],t[200005];
int ans[200005];
vector<int>v[205];
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		cin>>p[i].first;
		p[i].second=i;
	}
	for(int i=1;i<=m;++i)
	{
		cin>>val[i];
	}
	sort(p+1,p+1+n);
	for(int i=n;i>=1;--i)
	{
		int mxi=1;
		for(int j=2;j<=m;++j)
		{
			if(val[j]*(t[mxi]+p[i].first)>val[mxi]*(t[j]+p[i].first))
			{
				mxi=j;
			}
		}
		v[mxi].push_back(p[i].second);
		t[mxi]+=p[i].first;
	}
	for(int i=1;i<=m;++i)
	{
		cout<<v[i].size()<<' ';
		for(auto j:v[i])
		{
			cout<<j<<' ';
		}
		cout<<endl;
	}
}

Problem - B - Codeforces
榜有点歪啊,一眼秒了,但是开题太晚了写不完了。

发现每一个点的状态只有当前朝向哪边和打左/右闪/不打闪至多12种,换转向灯的转移边权为0,移动到下一个点的转移边权为1,01BFS即可。

Problem - H - Codeforces

\(d(i,j)\) 表示从 \((i,j)\) 往上有 \(d(i,j)\).,考虑矩形高度为 \(x\),将 \(d(i,j)\ge x\) 的位置在每一行并查集,得到一些极长线段,设 \(I_y\) 表示长度为 \(y\) 的极长线段个数,那么矩形 \(x\times y\) 的个数 \(w_y=I_y+2I_{y+1}+\cdots\)
\(c_y=I_y+I_{y+1}+\cdots\),有 \(c_y=I_y+c_{y+1},w_y=c_y+w_{y+1}\)
实际上是枚举了最后一行的位置,保证高度的情况下对一种长度计数。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 9e6 + 5;
int r, c, q;
string s[N];
vector< pair<int, int> > h[N];
vector< pair<int, int> > p[N];

ll I[N], C[N], R[N], ans[N];

int f[N], Size[N];

int find(int x){ return (f[x] == x) ? f[x] : (f[x] = find(f[x])); }

void merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x == y) return ;
    f[y] = x, Size[x] += Size[y];
}

void solve()
{
    r = read(), c = read(), q = read();
    for(int i = 1; i <= r; ++i) cin >> s[i];
    for(int i = 1; i <= q; ++i)
    {
        int x = read(), y = read();
        h[x].emplace_back(pair<int, int>(y, i));
    }
    for(int i = 1; i <= c; ++i)
    {
        int x = 0;
        for(int j = 1; j <= r; ++j)
        {
            if(s[j][i - 1] == '#') x = 0;
            else ++x;
            p[x].emplace_back(pair<int, int>(j, i));
        }
    }
    for(int i = r * c; i >= 1; --i) f[i] = i, Size[i] = 0;
    for(int i = r; i >= 1; --i)
    {
        for(auto [x, y] : p[i])
        {
            Size[(x - 1) * c + y] = 1;
            if(y > 1 && Size[(x - 1) * c + y - 1])
            {
                int fa = find((x - 1) * c + y - 1);
                I[Size[fa]]--;
                merge(fa, (x - 1) * c + y);
            }
            if(y < c && Size[(x - 1) * c + y + 1])
            {
                int fa = find((x - 1) * c + y + 1);
                I[Size[fa]]--;
                merge(fa, (x - 1) * c + y);
            }
            int fa = find((x - 1) * c + y);
            I[Size[fa]]++;
        }
        for(int j = c; j >= 1; --j)
        {
            C[j] = C[j + 1] + I[j];
            R[j] = C[j] + R[j + 1];
        }
        for(auto [x, id] : h[i])
        {
            ans[id] = R[x];
        }
    }
    for(int i = 1; i <= q; ++i) printf("%lld\n", ans[i]);
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}
posted @ 2025-04-07 00:13  梨愁浅浅  阅读(27)  评论(1)    收藏  举报