T1:
带权并查集。每个约束看做一条x连向y的边,边权为x+y = c
对于一个x,可能有多个y与他有关系,间接可以算出y与y’的关系,那么边权就记为y - y'
每次加入条件,就看看是否冲突
细节详见代码
据说正解是差分约束??苟蒻是真不会。。
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 q;
queue 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;
}