在这片梦想之地,不堪回首的过去像泡沫一样散|

PassName

园龄:3年1个月粉丝:32关注:16

ARC168

ARC168

前言

输输输,只有 A、B、D 独立做出来了。C 想到了的 idea,但是指数是 6 次方级别的,没敢写。E 看出来了是 wqs 二分,但是找不到凸,F 根本不可做。麻了。

[ARC168A]

传送门 link

这种题放在 A 就别瞎想,简单问题简单解决,双指针扫一遍即可。

int n;
string s;
int ans;

signed main()
{
    cin >> n >> s;
    
    for (rint i = 0, j = 0; i < n - 1; i++)
	{
    	if (s[i] != '>') continue;
		j = max(i, j);
		bool flag = 0;
		while (s[j] == '>') j++, flag = 1;
		j -= flag;
		ans += j - i + 1;
	}
	
	cout << ans << endl;
	
	return 0;
}

[ARC168B] Arbitrary Nim

传送门 link

比较板子的一个 nim 博弈

先判一手有没有必胜策略,如果有,考虑如何维护答案。镜子影像思想,先将出现两次的数给消掉,通过模仿对方的行为消除这两堆。保证最大的一堆用两步消掉。对最大的一堆异或记录即可。


multiset<int> s;
int n, t;

signed main()
{
	cin >> n;
	
	for (rint i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		t ^= x;//判断是否有必胜策略
		if (s.find(x) != s.end()) s.erase(x);
		else s.insert(x);
	}
	
	if (t) cout << "-1" << endl;
	else 
	{
	    if(s.empty())
		{
			cout << "0" << endl;
			exit(0);
		} 	
		cout << *--s.end() - 1 << endl; 
	}
    
    return 0;
} 

[ARC168C] Swap Characters

传送门link

根本没想到换个角度解决问题。以为肯定可以 dp 或者能推出一个比较快的公式,打死也想不到正解。

正面解决根本不会,那么正难则反,已知一个目标字符串 T,求从原来的字符串 s 变换至 T 的最小操作数是多少

考虑枚举 ab,ba,ac,ca,bc,cb 的位置数,判断最小交换次数是否小于等于 k。但是这种复杂度是非常高的,显然过不去。

考虑另一种枚举方法,我们先枚举 a,b 之间,b,c 之间以及 a,c 之间的交换次数,每次交换对最小步数的贡献为 1,设它们为 A,B,C。则 ab,ba 的次数均为 Aac,ca 的次数均为 Cbc,cb 的次数均为 B,再枚举全排列交换次数(显然每次交换对最小步数的贡献为 2),设为 D

那么 D 只可能被加到 ab,bc,ca 或者 ac,cb,ba 的次数里。剩下的情况都是这两种情况的其中一种的全排列。

所以直接枚举 A,B,C,D,再枚举 D 被加到前者的情况里还是后者的情况里,每次加进答案的贡献数就是组合数。复杂度 O(k4)

const int N = 2.5e5 + 5;
const int mod = 998244353;

int n, k, cnt[3], ans;
int fac[N], inv[N], ifac[N];
int ab, ac, bc, ba, ca, cb;

int read() 
{
    int x = 0;
    char c = getchar(), f = 0;

    while (c < '0' || c > '9')
        f |= (c == '-'), c = getchar();

    while (c >= '0' && c <= '9')
        x = (x << 3) + (x << 1) + (c & 15), c = getchar();

    return f ? -x : x;
}

int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = res * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return res;
}

void init()
{
    fac[0] = ifac[0] = 1;
    for (rint i = 1; i < N; i ++)
    {
        fac[i] = fac[i - 1] * i % mod;
        ifac[i] = ifac[i - 1] * qpow(i, mod - 2) % mod; 
    }	
}

int C(int a, int b)
{
	if (b < 0 || a < b) return 0;
	return fac[a] * ifac[a - b] % mod * ifac[b] % mod;
}

void update()
{
    ans = (ans + C(cnt[0], ab) * C(cnt[0] - ab, ac) % mod * C(cnt[1], ba) % mod * C(cnt[1] - ba, bc) % mod * C(cnt[2], ca) % mod * C(cnt[2] - ca, cb) % mod) % mod;
}

signed main() 
{
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	n = read();
	k = read();
    init();

    while (n--) cnt[getchar() - 'A']++;

    for (rint d = 0; d <= (k >> 1); d++)
      for (rint a = 0; a <= k - (d << 1); a++)
        for (rint b = 0; b <= k - (d << 1) - a; b++)
          for (rint c = 0; c <= k - (d << 1) - a - b; c++) 
			{
                ab = a;
				ac = b + d; 
				bc = c;
				ba = a + d;
				ca = b;
				cb = c + d;
				update();

                if (d)
                {
                    ab = a + d;
					ac = b; 
					bc = c + d;
					ba = a;
					ca = b + d; 
					cb = c; 
					update();						
				}
            }

    cout << ans << endl;
    
    return 0;
}

[ARC168D] Maximize Update

传送门link

要么是计数 dp,要么是区间 dp。第一眼想到的是 区间 dp

fi,j 为把 [i,j] 全部涂黑,其他为白的最多操作次数

fi,j=maxk=ir1fi,k+fk+1,r

fi,j=maxk=irfi,k1+fk+1,r+cost(l,r)

cost 表示区间内是否有覆盖该格子

然后前缀和处理一下就可以,剩下的板子。

复杂度 O(n3)

bool cost(int l, int r, int k)
{
	return bool(s[k][r] - s[k][k - 1] - s[l - 1][r] + s[l - 1][k - 1]);
}

signed main()
{
	cin >> n >> m;
	
	for (rint i = 1; i <= m; i++)
	{
		int a, b;
		cin >> a >> b;
		s[a][b]++; 
	} 
	
	for (rint i = 1; i <= n; i++)
		for (rint j = 1; j <= n; j++)
			s[i][j] += s[i - 1][j];
			
	for (rint i = 1; i <= n; i++)
		for (rint j = 1; j <= n; j++)
			s[i][j] += s[i][j - 1];
	
	for (rint len = 1; len <= n; len++)
	{
		for (rint l = 1; l <= n; l++)
		{
			int r = l + len - 1;
			for (rint k = l; k < r; k++)
			{
				f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r]);
			}
			for (rint k = l; k <= r; k++)
			{
				f[l][r] = max(f[l][r], f[l][k - 1] + f[k + 1][r] + cost(l, r, k));
			}
		}		
	}

	cout << f[1][n] << endl;
	
	return 0;
}

[ARC168E] Subsegments

传送门link

先二分答案转为判定性问题。这时候问题就被转化为,在令答案为 x 的前提下,是否能够划分出 k 个连续段。

fi 表示选出 i 段的最小代价,每一段的代价使用 rl 来刻画

fi 是求出恰好答案为 i 的方案数,这个函数是凸的。到了这一步就简单了。对 f 进行 wqs 二分。转移不选当前点,或选以当前点为右端点的代价最小的一个区间,从对应位置转移。

复杂度 O(nlog2n)

int n, k, S;
pair<int, int> f[N];
int p[N];
int a[N], s[N];

void F(int x) 
{
    for (rint i = 1; i <= n; i++) 
	{
        f[i] = f[i - 1];
        if (p[i])
        {
            f[i] = min(f[i], {f[p[i] - 1].x + (i - p[i]) - x, f[p[i] - 1].y + 1});			
		}
    }
}

bool check(int x) 
{
    int l = 1, r = n;
	int ans = 0;

    while (l <= r) 
	{
        int mid = (l + r) >> 1;
        F(mid);
        if (f[n].y <= x) l = mid + 1, ans = mid;
        else r = mid - 1;
    }
    F(ans);
    return f[n].x + ans * x <= n - k;
}

signed main() 
{
    cin >> n >> k >> S;

    for (rint i = 1; i <= n; i++)
    {
        cin >> a[i];
		s[i] = s[i - 1] + a[i];		
	}

    for (rint i = 1, j = 0; i <= n; i++) 
	{
        while (s[i] - s[j] >= S) j++;
        p[i] = j;
    }

    int l = 1, r = k;
    int ans = 0;

    while (l <= r) 
	{
        int mid = (l + r) >> 1;
        if (check(mid)) ans = mid, l = mid + 1;
        else r = mid - 1;
    }

    cout << ans << endl;
    
	return 0;
}

[ARC168F] Up-Down Queries

传送门 link

不可做,根本不可做。看了题解代码也打不出来,写了一个小时直接弃了。

这道题的前置知识是省选联考2023人员调动。这种题留着,下辈子有机会一定补上。

本文作者:PassName

本文链接:https://www.cnblogs.com/spaceswalker/p/17973331

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   PassName  阅读(38)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起