dp

t1:

给一个串,问多少子序列构成的数字可以被\(3\)整除

\(f[i][j]\)表示前\(i\)个模\(3\)余数为\(j\)的方案数

现在的+直接继承原来的方案,注意初始化

int f[N][3]; char s[N];
int main(){
	scanf("%s",s + 1);
	int n = strlen(s + 1);
	for(int i = 1;i <= n;++i)
		f[i][(s[i] - '0') % 3] = 1;
	for(int i = 1;i <= n;++i)
		for(int j = 0;j < 3;++j)
			for(int k = i+1;k <= n;++k)
				f[k][(j + s[k]-'0') % 3] = (f[k][(j+s[k]-'0')%3] + f[i][j]) % mod;
	for(int i = 1;i <= n;++i)
		f[0][0] = (f[0][0] + f[i][0]) % mod;
	printf("%d",f[0][0]);
	return 0;
}

t2:

给你一个合法的括号序列\(s1\),每次你可以删除一个\("()",\)你可以删除\(0\)个或者多个\("()"\),求能否删成另一个括号序列\(s2\)

\(f[i][j]\)表示字符串前\(i\)个字符删除\(()\)操作,能否匹配到\(t\)的前\(j\)个字符

\(s[i]==t[j]\),不用删除当前\(s[i]\)\(f[i][j]|=f[i-1][j-1]\)

\(s[i]==)\) 尝试删除\(s[i]\)结尾\()\)最小合法字符串序列 假设到\(k\) \(f[i][j]~~|=~~f[k][j]\)

bitset<N>f[N];
int main(){
	string s,t;
	cin >> s >> t;
	int slen = s.size(),tlen = t.size();
	f[0][0] = 1;
	for(int i = 1;i <= slen;++i)
		for(int j = 1;j <= tlen;++j){
			if(s[i-1] == t[j-1]) f[i][j] = f[i][j] | f[i-1][j-1];
			if(s[i-1] == ')'){
				int k = i-1,cnt = 1;
				while(cnt > 0){
					if(s[k-1] == ')') ++cnt;
					else --cnt;
					--k;
				}
				f[i][j] = f[i][j] | f[k][j];
			}
		}
	if(f[slen][tlen]) puts("Possible");
	else puts("Impossible");
}

t3:

Jason 正在打一场 \(CF\)
比赛时间为\(T\)分钟,有\(N\)道题,可以在比赛时间内的任意时间提交代码
\(i\)道题的起始分数为\(maxPoints[i]\),题目的分数随着比赛的进行,每分钟减少\(pointsPerMinute[i]\)
这是一场比较\(dark\)\(CF\),分数可能减成负数
已知第\(i\)道题需要花费 \(requiredTime[i]\)的时间解决
请问最多可以得到多少分

第一行输入两个整数\(N,T (1 ≤ N ≤ 50, 1 ≤ T ≤ 100000)\)
第二行输入\(n\)个整数\(maxPoints[i]\)
第三行输入\(n\)个整数\(pointsPerMinute[i]\)
第四行输入\(n\)个整数\(requiredTime[i]\)
\(1 ≤ maxPoints[i],pointsPerMinute[i],requiredTime[i] ≤ 100000\)

1 74
502
2
47
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 55,T = 1e5 + 5;
#define int long long
int f[T];
struct node{
	int maxx;//最大分数 
	int pm;//每分钟减小的分数
	int re;//需要的时间
	bool operator < (const node &x) const{
		return pm*1.0/re > x.pm*1.0/x.re;
	}
}e[N]; int n,t;
signed main(){
//	freopen("text0.in","r",stdin);
//	freopen("text0.out","w",stdout);
	scanf("%lld%lld",&n,&t);
	for(int i = 1;i <= n;++i) scanf("%lld",&e[i].maxx);
	for(int i = 1;i <= n;++i) scanf("%lld",&e[i].pm);
	for(int i = 1;i <= n;++i) scanf("%lld",&e[i].re);
	sort(e + 1,e + n + 1); int ans = 0;
	for(int i = 1;i <= n;++i)
		for(int j = t;j >= e[i].re;--j){
			f[j] = max(f[j],f[j - e[i].re] + e[i].maxx - e[i].pm*j);
			ans = max(f[j],ans);
		}
	printf("%lld",ans);
}

t4

给你一个完全二分图,图中左右两边顶点数相同,每条边染成红绿蓝中一种,满足任意红边不共享端点,任意蓝边不共享端点,求满足条件的染色数 \((n\le1e7)\)

可以把左右\(n\)个点的二分图题转化为\(n\times n\)的棋盘问题,题意转化成在\(n\times n\)棋盘放红蓝棋子,使任意行列不能有相同颜色棋子方法(但是我没转化qwq)

我们先默认所有边是绿色的,接下来,我们尝试把一些边染成红蓝色,

定义\(f_i\)表示二分图其中一边有\(i\)个点,并且边只有绿色和红色时的答案。 \(f_0=1,f_1=2\)

考虑如何递推,\(f_{i-1}\to f_i\)会新增两个点

分情况考虑:

1.这对点只连绿边,方案数为\(f_{i-1}\)

2.两点连一条红边,现在只能连绿边了,方案数为\(f_{i-1}\)

3.这对点间连绿边,但至少一个点连了红边

这对边中的一个点,与之前的一个点之前连了一条红边,那么,其实就相当于还剩\(i-1\)对待连

方案数为\(2\times(i-1)\times f(i-1)\) (两个点选一个,\(i-1\)个点对选一对)

但是我们发现,对于左边和右边连了红边的情况,我们算了两次,所以我们还要减去两边都连红边的情况,减去

\((i-1)^2\times f(i-2)\)

\((i-1)^2\) 意义为各选\((i-1)\)对点

\(f_i=2\times i\times f(i-1)-(i-1)^2\times f(i-2)\)

再考虑有蓝边的情况,注意到蓝边和红边本质相同,直接容斥即可

\(ans=\sum\limits_{k=0}^n(-1)^k(C_n^k)^2i!f(n-k)^2\)

只填一种颜色,对于选取\(i\)个节点每边都有\(\large f_n={n\choose i}A_n^i\),两边就是\((f_n)^2\)

t5

首先我们肯定是贪心的策略去做,就是我选了叶子节点开始染色,那么染过色的地方就尽量不要去选,这应该是最优策略。
但其实这样是有错误的
考虑这样一种情况 \(4\to 3\to 2\to 1\) 这是其中一条链,假设\(k_1=1,k_2=1,k_3=3,k_4=2\)
按照上面的做法,我们从\(4\)开始染色,ok,\(3\)\(4\)已经染色成功,然后继续\(2\)开始染色,ok,\(2\)染色成功,最后把根染色这样操作次数要\(3\)次。其实正确答案应该是\(2\).
\(4\)开始染色,\(3\)\(4\)覆盖,\(3\)染色,\(1\)\(2\)覆盖,结束
所以我们不仅要维护染色的最远距离,同时还需要去更新掉父亲节点的染色距离,
比如在上述例子,\(k[2]=\max(k[2],k[3]-1)=k[3]-1=2\) 这样就可以避免上述错误了。
\(dfs\)维护染色最远距离,并且把子节点的染色范围更新到父亲节点去取最大值即可。
如果到当前节点无法被子树中节点的染色覆盖,那么就要将当前节点染色。

int n,ans,f[N]; vector<int>g[N]; int k[N];
void dfs(int x,int fa){
	for(int i = 0;i < g[x].size();++i){
		int v = g[x][i]; dfs(v,x);
		k[x] = max(k[x],k[v] - 1);//注意k数组,我们不关心这个最远来自谁,只要知道可以最远多少就行了
		f[x] = max(f[x],f[v] - 1);//还可以往上延申长度
	}
	if(!f[x])//如果子节点都无法覆盖到这个点
		++ans,f[x] = k[x];
}
int main(){
	n = read(); int u;
	for(int i = 2;i <= n;++i){
		u = read(); g[u].push_back(i);
	}
	for(int i = 1;i <= n;++i) k[i] = read();
	dfs(1,0); printf("%d",ans);
}
posted @ 2020-10-29 18:16  INFP  阅读(160)  评论(0编辑  收藏  举报