2016长乐夏令营 Day9

18 篇文章 0 订阅
14 篇文章 0 订阅

T1:

带权并查集。每个约束看做一条x连向y的边,边权为x+y = c

对于一个x,可能有多个y与他有关系,间接可以算出y与y’的关系,那么边权就记为y - y'

每次加入条件,就看看是否冲突

细节详见代码

据说正解是差分约束??苟蒻是真不会。。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 1E3 + 10;

int n,m,k,fa[maxn*2],va[maxn*2],x[maxn],y[maxn],c[maxn];

int fat(int now)
{
	if (now == fa[now]) return now;
	int ret = fat(fa[now]);
	va[now] = va[now] + va[fa[now]];
	return fa[now] = ret;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#else
		freopen("matrix.in","r",stdin);
		freopen("matrix.out","w",stdout);
	#endif
	
	int t; cin >> t;
	while (t--) {
		scanf("%d%d%d",&n,&m,&k);
		for (int i = 1; i <= n+m; i++) fa[i] = i,va[i] = 0;
		for (int i = 1; i <= k; i++) scanf("%d%d%d",&x[i],&y[i],&c[i]);
		bool flag = 1;
		for (int i = 1; i <= k; i++) {
			int fx = fat(x[i]);
			int fy = fat(y[i]+n);
			if (fx == fy) {
				if (va[x[i]] - va[y[i]+n] != c[i]) {
					printf("No\n"); 
					flag = 0; break;
				}
			}
			else {
				fa[fx] = fy;
				va[fx] = va[y[i]+n] - va[x[i]] + c[i];
			}
		}
		if (flag) printf("Yes\n");
	} 
	return 0;
}

T2:

如果n == 1

以任意位置i作为第一个,那么还需要向上刷i-1次,向下刷m-i次

我们可以在m-i次的过程中任意插入i-1个向上刷的操作

即m-i个数之间放入i-1块隔板(板可以在序列前、后、中间任意位置)

板与物品共有m-i+i-1 = m-1个 于是C(m-1,i-1)

枚举i,统计答案

如果 n == 2

f[i]:刷一面2*i的墙,强制规定第一次必须刷第一行的方案数

f[i] = 2*f[i-1]*(2*i-1)

首先,第一次有两种刷法,然后要刷完底下2*(i-1)个,而还有第一行的另一个没有刷,这块墙壁可以在刷底下2*(i-1)块的过程中的任意时刻刷,于是有(2*i-1)个时刻可以刷

统计答案

第一次任意在第i行刷

只刷上面有f[i-1]种,只刷下面有f[m-i]种

上下都刷(隔板法)有C(m-2,2*(i-1))*f[i-1]*f[m-i]种

在这个过程中随时可以插入刷i行的另一块的操作

所以 2*C(m-2,2*(i-1))*f[i-1]*f[m-i]*(2*m-1)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<bitset>
using namespace std;

const int maxn = 3010;
typedef int LL;
const LL mo = 1000000007LL;

LL C[maxn*2][maxn*2],f[maxn];
int n,m,t;

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
    #else
		freopen("paint.in","r",stdin);
		freopen("paint.out","w",stdout);
	#endif
	
	f[0] = 1;
	for (int i = 1; i <= 3000; i++) 
		f[i] = 2LL*(2*i-1)*f[i-1]%mo;
	C[0][0] = 1;
	for (int i = 1; i <= 6000; i++) {
		C[i][0] = 1;
		for (int j = 1; j <= i; j++)
			C[i][j] = (C[i-1][j] + C[i-1][j-1])%mo;
	}
	
	cin >> t;
	while (t--) {
		scanf("%d%d",&n,&m);
		LL ans = 0;
		if (n == 1) {
			for (int i = 1; i <= m; i++)
				ans = (ans + C[m-1][i-1])%mo;
		}	
		else {
			for (int i = 1; i <= m; i++)
				ans = (ans + 2LL*(2*m-1)*C[2*m-2][2*i-2]%mo*f[i-1]%mo*f[m-i]%mo)%mo;
		}
		int Ans = ans;
		printf("%d\n",Ans);
	}
	return 0;
}

T3:

先建立AC自动机

考虑在AC自动机上dp

f[i][j]:原串走到第i个位置,AC自动机走到第j个点,最少需要更改几个

为了最小化ans,需要尽量贴着原序列走,尽管这样很容易碰到禁止的序列

枚举j的四个儿子,如果该儿子等于i+1位置上的字母

f[i+1][son] = f[i][j]

否则 f[i+1][son] = f[i][j]+1

当然,原来的AC自动机中有些点一定不能到达的,标出来即可

在转移失配边时要注意用|运算,标出后缀为禁止序列的情况(详见代码)

最后,AC自动机以1为根,对于走到0的情况,四个孩子都写1,否则会出错啊GG

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<bitset>
using namespace std;

const int maxn = 2E3 + 20;
const int INF = ~0U>>1;

struct AC{
	int ch[5],va;
	AC(){memset(ch,0,sizeof(ch)); va = 0;}
}tree[maxn];

struct P{
	int x,y;
	P(int _x = 0,int _y = 0) {x = _x; y = _y;}
};

int g[maxn][maxn];
int n,len,cur = 1,ans = INF,num[maxn],f[maxn];
char c[maxn];

queue <int> q;
queue <P> Q;

int Change(int pos)
{
	if (c[pos] == 'A') return 1;
	if (c[pos] == 'G') return 2;
	if (c[pos] == 'C') return 3;
	return 4;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
    #else
		freopen("dna.in","r",stdin);
		freopen("dna.out","w",stdout);
	#endif
	
	cin >> n;
	for (int i = 0; i < n; i++) {
		scanf("%s",c); len = strlen(c);
		int x = 1;
		for (int j = 0; j < len; j++) {
			int Num = Change(j);
			if (!tree[x].ch[Num]) tree[x].ch[Num] = ++cur;
			x = tree[x].ch[Num];
		}
		tree[x].va = 1;
	}
	
	q.push(1); for (int i = 1; i <= 4; i++) tree[0].ch[i] = 1;
	while (!q.empty()) {
		int k = q.front(); q.pop();
		for (int i = 1; i <= 4; i++) {
			int u = tree[k].ch[i];
			if (!u) {
				tree[k].ch[i] = tree[f[k]].ch[i];
				continue;
			}
			int v = f[k];
			while (v && !tree[v].ch[i]) v = f[v];
			f[u] = tree[v].ch[i];
			tree[u].va |= tree[f[u]].va;
			q.push(u);
		}
	}
	scanf("%s",c+1); len = strlen(c+1);
	for (int i = 1; i <= len; i++) num[i] = Change(i);
	
	for (int i = 0; i <= len; i++)
		for (int j = 0; j <= cur; j++)
			g[i][j] = INF;
	Q.push(P(0,0)); g[0][0] = 0;
	while (!Q.empty()) {
		P k = Q.front(); Q.pop();
		if (k.x == len) continue;
		for (int i = 1; i <= 4; i++)
			if (!tree[tree[k.y].ch[i]].va) {
				if (num[k.x+1] == i && g[k.x+1][tree[k.y].ch[i]] > g[k.x][k.y]) {
					if (g[k.x+1][tree[k.y].ch[i]] == INF) Q.push(P(k.x+1,tree[k.y].ch[i]));
					g[k.x+1][tree[k.y].ch[i]] = g[k.x][k.y];
				}
				if (num[k.x+1] != i && g[k.x+1][tree[k.y].ch[i]] > g[k.x][k.y] + 1) {
					if (g[k.x+1][tree[k.y].ch[i]] == INF) Q.push(P(k.x+1,tree[k.y].ch[i]));
					g[k.x+1][tree[k.y].ch[i]] = g[k.x][k.y]+1;
				}
			}
	}
	
	for (int i = 0; i <= cur; i++) ans = min(ans,g[len][i]);
	if (ans == INF) cout << -1;
	else cout << ans; 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值