在这片梦想之地,不堪回首的过去像泡沫一样散去,不愿面对的明天也永远不会到来,人们为何选择沉睡?是因为害怕从梦中醒来。|

PassName

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

2024.5.18 杂题

2024.5.18 杂题

「SMOI-R1」Apple

两个操作,修改元素,求子集和。

高位前缀和不会。考虑朴素 dp 转移

f[i] 表示二进制下长度为 n 的数前 i 位为 1,后边为 0 的子集和。理论来说可以转移,但是比较麻烦,考虑优化状态,f[i][j] 表示前 n2 位为 in2 位为 j 的子集和。在插入一个 ai 时,枚举前 n2 位的超集 X,令 f[X][y]+=ai ,删除时减去即可。

在查询 i 的子集和时,枚举后 n2 的子集 Y,答案 Yyf[x][Y]

这些操作与树状数组很类似,仿照树状数组写一个数据结构优化掉,复杂度大概是 O(QN)

const int N = (1 << 20) + 10;
const int M = (1 << 10) - 1;

int n, q;
int a[N];

struct Tree
{
	int c[M + 2][M + 2];
    
	void add(int x, int y) 
	{
		int X = x >> 10, Y = x & M;
		if (n <= 10) {c[0][Y] += y; return ;}
		for (rint i = X; i <= M; i = (i + 1) | X) 
			c[i][Y] += y;
	}
		
	int query(int x) 
	{
		int X = x >> 10, Y = x & M, ans = 0;
		for (rint i = Y;; i = (i - 1) & Y) 
		{
			ans += c[X][i];
			if (!i) break;
		}
		return ans;
	}
		
	void clear(int x, int y) 
	{
		int X = x >> 10, Y = x & M;
		if (n <= 10) {c[0][Y] -= y; return ;}
		for (rint i = X; i <= M; i = (i + 1) | X)
			c[i][Y] -= y;
	}	
} tree;

signed main() 
{
    ios::sync_with_stdio(0);
    cin.tie(0);	
    cout.tie(0);
	cin >> n >> q;
	for (rint i = 0; i < (1 << n); i++) 
	{
		cin >> a[i];
		tree.add(i, a[i]);
	}
	while (q--) 
	{
		int op, x;
		cin >> op >> x;
		if (op == 1) 
		{
			cout << tree.query(x) << endl;
		} 
		else 
		{
			int y;
			cin >> y;
			tree.clear(x, a[x]);
			a[x] = y;
			tree.add(x, a[x]);
		}
	}
	return 0;
}

『STA - R5』RDG

不会博弈论,考虑 dp

fi,j 表示考虑从 aian 这一段,前面进行了 j1 操作的情况下先手能不能赢。a 需要从小到大排序。

fi,j={1j>aifi+1,jj=ai¬fi+1,jor¬fi,j+1j<ai

jai2 时为 01 相间。所以保留 O(1) 状态即可通过本题。

bool c(int i, int j) 
{
	if (i == n || j > a[i]) return 1;
	if (j < a[i] - 3) j += (a[i] - 2 - j) / 2 * 2;
	if (!v[i][a[i] - j]) 
	{
		v[i][a[i] - j] = 1;
		if (a[i] == j) f[i][a[i] - j] = c(i + 1, j);
		else f[i][a[i] - j] = !(c(i + 1, j) & c(i, j + 1));
	}
	return f[i][a[i] - j];
}

signed main() 
{
	int T;
	cin >> T;
	while (T--)
	{
		cin >> n;
		for (rint i = 1; i <= n; i++) cin >> a[i];
		for (rint i = 1; i <= n; i++) 
		    for (rint j = 0; j <= 3; j++) 
			    f[i][j] = v[i][j] = 0;
		sort(a + 1, a + n + 1);
		if (c(1, 0)) cout << "Alice" << endl;
		else cout << "Bob" << endl;
	}
	return 0;
}

CF1965E

毒瘤构造,虽然尺子姐已经在题解区给出了构造方案,但我还是想自己去搭一搭,于是找 hanss6 要了个 MC。

图中玻璃表示临时空气,混凝土即为不同色方块。

最上层所有混凝土均至少紧挨着一格空气,只需把所有空气替换成一种颜色的混凝土就连通

int n, m, k;
int a[N][N], x[N][N], y[N][N];
vector<tuple<int, int, int, int>> s;

signed main() 
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);	
    cin >> n >> m >> k;
    for (rint i = 1; i <= n; i++)
		for (rint j = 1; j <= m; j++)
			cin >> a[i][j];
	for (rint i = 1; i <= n; i++)
	{
		for (rint j = 1; j <= m; j++)
		{
			x[i][j] = i;
			y[i][j] = j;
		}
	}
    for (rint z = 1; z <= m; z++)
	{
        if(z >= 2) 
		{
            for (rint i = 1; i <= n; i++)
				for (rint j = 1; j <= m; j++) 
				    s.ep(x[i][j], y[i][j], z, a[i][j]);
        }
        for (rint i = 1; i <= n; i++)
            for (rint j = 1; j <= m; j++)  
			{
                if(j == m - z + 1) 
				{
                    for (rint y = j + 1; y <= 2 * j - 1; y++)
					    s.ep(i, y, z, a[i][j]);
                    x[i][j] = i;
                    y[i][j] = 2 * j - 1;
                }
            }
    }
    int _m = m;
    for (rint p = 1; p <= k; p++)
	{
        for (rint i = 1; i <= n + 1; i++) 
		{
			for (rint j = 1; j <= 2 * m - 1; j++) 
			{
                if (i == n + 1) 
				    s.ep(i, j, _m, p);
                else if (j % 4 == 2 || j >= 2 * m - 3 && !(j % 2)) 
				    s.ep(i, j, _m, p);
                else if ((j & 1) && _m != m && a[i][(j + 1) / 2] >= p) 
				    s.ep(i, j, _m, a[i][(j + 1) / 2]);
            }
        }
        _m++;
    }
    cout << s.size() << endl;
    for (auto it : s)
    {
    	cout << get<0>(it) << " ";
		cout << get<1>(it) << " ";
		cout << get<2>(it) << " ";
		cout << get<3>(it) << endl;
	}
    return 0;
}

饥饿的奶牛

考虑直接枚举每一个点。设 f[i] 为从开始到 i 时的答案。用 vector 存区间。对于区间 ai

f[si.r]=max0jsi.l{f[j]}

int n;
int f[N];
vector<int> a[N];

signed main()
{
    cin >> n;
	for (rint i = 1; i <= n; i++)
	{
		int x, y;
		cin >> x >> y;
		a[y].push_back(x);
	}
	for (rint i = 0; i <= M; i++)
	{
		f[i] = f[i - 1];
		int k = a[i].size();
		for (rint j = 0; j < k; j++)
		{
			int val = i - a[i][j] + 1;
			if (a[i][j] >= 1) f[i] = max(f[i], f[a[i][j] - 1] + val);
			else f[i] = max(f[i], val);
		}
	}
	cout << f[M] << endl;
	return 0;
}

[HAOI2006] 数字序列

第一问,考虑不严格上升的序列,则为 nlen(LIS),严格上升,则 ijaiaj,移项,iaijaj,将 ai > aii 按照同样方法处理即可。(在代码中对于第一问的 dp 使用 g[] 表示)

对于第二问,我们设 f[i] 为开始到 i 的答案,则

f[i]=maxg(j)+1=g(i){f(j)+w(j+1,i)}

对于区间 [i,j] k 使得 [i,k]=ai, [k+1,j]=aj,并且 k 使得其为最优策略,对于每个 w 暴力 k 的位置更新答案。

int n, a[N], b[N], len;
int g[N], f[N], c1[N], c2[N];
vector<int> vec[N];

signed main() 
{
	cin >> n;
	for (rint i = 1; i <= n; i++)
	{
		cin >> a[i];
		a[i] -= i;
	}
	n++;
	a[n] = inf;
	b[++len] = a[1];
	g[1] = 1;
	for (rint i = 2; i <= n; i++) 
	{
		if (a[i] >= b[len]) b[++len] = a[i], g[i] = len;
		else 
		{
			int pos = upper_bound(b + 1, b + 1 + len, a[i]) - b; 
			b[pos] = a[i];
			g[i] = pos;
		}
	}
	cout << n - len << endl;

	a[0] = -inf;
	vec[0].push_back(0);
	for (rint i = 1; i <= n; i++) 
	{
		f[i] = inf;
		for (rint j = 0; j < vec[g[i] - 1].size(); j++) 
		{
			int v = vec[g[i] - 1][j];
			if (a[v] > a[i]) continue;
			c1[v - 1] = c2[v - 1] = 0;
			for (rint k = v; k <= i; k++)
			{
				c1[k] = abs(a[k] - a[v]);
				c2[k] = abs(a[k] - a[i]);
			} 
			for (rint k = v + 1; k <= i; k++)
			{
				c1[k] += c1[k - 1];
				c2[k] += c2[k - 1];
			} 
			for (rint k = v; k <= i; k++) 
			{
				int val = c1[k] - c1[v] + c2[i] - c2[k];
				f[i] = min(f[i], f[v] + val);
			}
		}
		vec[g[i]].push_back(i);
	}

	cout << f[n] << endl;

	return 0;
}

SP9941 GRE

fi 表示以 i 开头的最长子序列,则 fi=maxj(i,n]si ∈sjfj+wi

用哈希表存每个串 sifi。枚举每一个串在他之前所有出现过的串的长度 l,对于每一个 l 找出该串所有长度为 l 的子串,在哈希表里查询即可实现转移。


int T, n;
int ans, v[N], len[N], f[N], s[M];
char c[M];
int *h[N], p[M], tab[M], maxx[M];

int get(int x) 
{
	int p = x % lim;
	while (1)
	{
		if (tab[p] == -1 || tab[p] == x) break;
		p++;
		p %= lim;
	}
	return p;
}

int Hash(int i, int l, int r) 
{
	return (h[i][r] - h[i][l - 1] * p[r - l + 1] % mod + mod) % mod;
}

signed main() 
{
	cin >> T;
	p[0] = 1;
	for (rint i = 1; i < M; i++)
	{
		p[i] = p[i - 1] * base % mod;
	}
	int idx = 1;
	while (T--)
	{
		memset(tab, -1, sizeof tab);
		memset(maxx, 0, sizeof maxx);
		cin >> n;
		ans = 0;
		for (rint i = 1; i <= n; i++) 
		{
			scanf("%s", c + 1);
			cin >> v[i];
			len[i] = strlen(c + 1);
			f[i] = v[i];
			h[i] = (int*)malloc(sizeof(int) * len[i] + 3);
			for (rint j = 1; j <= len[i]; j++)
			{
				h[i][j] = (h[i][j - 1] * base % mod + c[j] - 'a' + 1) % mod;
			}
			for (rint j = 1; j <= len[i]; j++)
			{
			    if (s[j])
				{
				    for (rint k = 1; k + j - 1 <= len[i]; k++) 
					{
						int x = get(Hash(i, k, k + j - 1));
						if (tab[x] != -1) f[i] = max(f[i], maxx[x] + v[i]);
					}				
				}				
			}
			
			int p = get(Hash(i, 1, len[i]));
			tab[p] = Hash(i, 1, len[i]);
			maxx[p] = max(maxx[p], f[i]);
			ans = max(ans, f[i]);
			s[len[i]]++;
		}
		int max_len = 0;
		for (rint i = 1; i <= n; i++)
		{
			if (i != n) max_len = max(max_len, len[i]);
			else
			{
				for (rint j = 1; j <= max(max_len, len[i]); j++)
				    s[i] = 0;
			}
		}
		printf("Case #%lld: %lld\n", idx, ans);
		idx++;
	}
	return 0;
}

P3766 核心密码B

对于 ab,对每个 b 单独算贡献。

如果 b3,可以不动脑子的朴素实现。

对于 b=2,暴力 a107 时直接暴力处理。把 1x2 变为 1(x1)x+1x(x+1)2 取近似值,直接裂项。要求
x=107+1qi12(1x11x+1)=1107+1107+11qi11qi

void solve(int x) 
{
	ll cnt = 2, r = qpow(cnt, x);
	lb sum = 0;
	for (rint i = 1; i <= n; i++) 
	{
		while (r <= q[i].x) 
		{
			lb tmp = 1.0;
			tmp /= r;
			sum += tmp;
			cnt++;
			r = qpow(cnt, x);
		}
		s[q[i].id] += sum;
	}
}

signed main() 
{
	cin >> n;
	for (rint i = 1; i <= n; i++)
	{
		cin >> q[i].x; 
		q[i].id = i;		
	}
	sort(q + 1, q + n + 1, cmp);
	for (rint i = 3; i <= 63; i++) solve(i);
	for (rint i = 1; i <= n; i++) 
	{
		if (q[i].x > inf) 
		{
			int x = sqrtl(q[i].x);
			lb t1 = 1.0, t2 = 1.0, t3 = 1.0, t4 = 1.0;
			t1 /= (lb)(1e7);
			t2 /= (lb)(1e7 + 1);
			t3 /= (lb)(x);
			t4 /= (lb)(x + 1);
			t1 = t1 * 1.0 + t2 * 1.0 - t3 * 1.0 - t4 * 1.0;
			t1 = t1 * 1.0 / (lb)(2.0);
			s[q[i].id] += t1;
			q[i].x = inf;
		}
	}
	solve(2);
	for (rint i = 1; i <= n; i++)
	{
		cout << setprecision(15) << fixed << s[i] << endl;		
	}
    return 0;
}

CF149D

定义 fi,j,0 or 1or 2,0 or 1or 2 为区间 [i,j]i 不染/染成红色/染成蓝色,j 同理的方案数。分类讨论直接转移即可。

stack<int> s;
int n;
int f[N][N][3][3], mp[N];
//mp 存储对应的括号位置
char a[N];

void dfs(int l, int r) 
{
	if (l + 1 == r) 
	{
		f[l][r][0][1] = f[l][r][0][2] = f[l][r][1][0] = f[l][r][2][0] = 1;
		return ;
	}
	if (mp[l] == r) 
	{
		dfs(l + 1, r - 1);
		for (rint i = 0; i <= 2; i++) 
		{
			for (rint j = 0; j <= 2; j++) 
			{
				if (i != 1)	f[l][r][1][0] += f[l + 1][r - 1][i][j], f[l][r][1][0] %= mod;
				if (i != 2)	f[l][r][2][0] += f[l + 1][r - 1][i][j], f[l][r][2][0] %= mod;
				if (j != 1)	f[l][r][0][1] += f[l + 1][r - 1][i][j], f[l][r][0][1] %= mod;
				if (j != 2)	f[l][r][0][2] += f[l + 1][r - 1][i][j], f[l][r][0][2] %= mod;
			}
		}
	} 
	else 
	{
		dfs(l, mp[l]);
		dfs(mp[l] + 1, r);
		for (rint i = 0; i <= 2; i++)	
		  for (rint j = 0; j <= 2; j++)	
			for (rint k = 0; k <= 2; k++)	
			  for (rint p = 0; p <= 2; p++)	
				if (!(j && j == k))	
				{
					f[l][r][i][p] += f[l][mp[l]][i][j] * f[mp[l] + 1][r][k][p] % mod;
					f[l][r][i][p] %= mod;
				}
	}
}

signed main() 
{
	scanf("%s", a + 1);
	n = strlen(a + 1);
	for (rint i = 1; i <= n; i++) 
	{
		if (a[i] == '(') s.push(i);
		else	
		{
			mp[s.top()] = i; 
			s.pop();
		}
	}
	dfs(1, n);
	int ans = 0;
	for (rint i = 0; i <= 2; i++)
	{
		for (rint j = 0; j <= 2; j++)
		{
			ans += f[1][n][i][j];
			ans %= mod;
		}
	}	
	cout << ans << endl;
	return 0;
}

本文作者:PassName

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

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

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