福建省队集训 20180710

福建省队集训 20180710

matrix

感觉直接对偶+费用流吧。

考虑直接把一行或者一列+1,显然还是满足条件的,至于初始条件直接全0

然后每个合法状态之间的变换一定可以被表示成每行/列加几次,如果拆出来还有剩的话一定是不合法的。

那么转化成这个问题:设(p_x)为这一行加了多少次,(h_{x})为这一列加了多少次

考虑每个格子的数,(p_i+h_{j}geq w_{i,j}),这是lp的约束

然后考虑最后要求的方案,(operatorname{minimize} n*left(sum_ip_i+sum_ih_i ight))

这不就是对偶之后的标准形式了吗,所以我猜是对的

关于对偶:

[operatorname{minimize} sum_ip_i+sum_ih_i\ operatorname{s.t} left{egin{array}{l}p_i+h_jgeq w_{i,j}\ p_i,h_igeq 0 end{array} ight. ]

变成

[egin{array}{c} operatorname{maximize} sum_{(u, v)} w_{u v} d_{u v} \ ext {s.t}left{egin{array}{l} sum_{v} d_{u v} leq 1, u in X \ sum_{v} d_{u v} leq 1, v in Y end{array} ight. end{array} ]

如果闲的无聊也可以把度数矩阵和w和一排1的矩阵拿来用数学角度考虑...

然而费用流我不知道怎么输出方案(群里EI和myh说可以直接二分图的最小覆盖,但是我不知道怎么覆盖),还是用KM吧。

第一次写的时候没保存就关机了,我是SB

paper

或许是做完T1看了一眼题解瞟到了啥启发了一下,反正想出来了,看来以后还是全部做完再看题解

首先考虑行不管怎么折之后都不影响列的匹配,因为折之后相当于把相同的状态缩在了一起。

那么行和列就是独立的,考虑分别计算能缩成的区间的方案数

考虑manacher求出列的以当前列为中点的最大回文串长度,那么这个点的状态可以从这个区间里继承,记录一个后缀就能快速判断了。然后对于答案求一个前缀和再枚举右端点。

#include<bits/stdc++.h>
#define ll long long
#define ROF(i,a,b) for(int i=a;i>=b;--i)
#define FOR(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x; 
} 
const int N = 1002001;
int n,m;
vector<int>vec[N],vec2[N];
char s[N];
int tp,p[N],pre[N],suf[N],ansp[N],anss[N],top=0;
int check(int r,int l){
	if(l<0||r>tp+1) return 0;
	if(l%2==0) return 1;
	l=(l)/2,r=(r)/2;
	FOR(i,1,n){
		if(vec[i][l]!=vec[i][r]) return 0;
	}
	return 1;
}
ll manacher1(){
	tp=2*m-1;
	int mid=0,mr=0;top=0;
	pre[0]=pre[1]=1;
	FOR(i,1,tp){
		if(i<=mr) p[i]=min(p[(mid<<1)-i],mr-i);
		for(;check(i+p[i]+1,i-p[i]-1);++p[i]);
		if(i+p[i]>mr){
			mr=p[i]+i;
			mid=i;
		} 
		if(i%2==1) continue;
		int now=i/2;ansp[now+1]=ansp[now];
		if(pre[now-p[i]/2+1]){
			ansp[now+1]++;
			FOR(j,top,now+1) pre[j]=1;
			top=now; 
		}
	}
//	mid=tp+1;int ml=tp+1;
	top=m;suf[m]=1;
	ROF(i,tp,1){
		if(i%2==1) continue;
		int now=i/2;//anss[now]=anss[now+1];
		if(suf[now+p[i]/2]){
			anss[now]++;
			ROF(j,top,now) suf[j]=1;
			top=now; 
		}
	}
	anss[m]=1;
	ll ans=0;
	FOR(i,1,m){
		ans+=(ansp[i]+1)*anss[i];
	}
	return ans;
}
int check2(int r,int l){
	if(l<0||r>tp+1) return 0;
	if(l%2==0) return 1;
	l=(l)/2,r=(r)/2;
	FOR(i,1,n){
		if(vec2[i][l]!=vec2[i][r]) return 0;
	}
	return 1;
}
ll manacher2(){
	tp=2*m-1;
	int mid=0,mr=0;top=0;
	pre[0]=pre[1]=1;
	FOR(i,1,tp){
		if(i<=mr) p[i]=min(p[(mid<<1)-i],mr-i);
		for(;check2(i+p[i]+1,i-p[i]-1);++p[i]);
		if(i+p[i]>mr){
			mr=p[i]+i;
			mid=i;
		} 
		if(i%2==1) continue;
		int now=i/2;ansp[now+1]=ansp[now];
		if(pre[now-p[i]/2+1]){
			ansp[now+1]++;
			FOR(j,top,now+1) pre[j]=1;
			top=now; 
		}
	}
//	mid=tp+1;int ml=tp+1;
	top=m;suf[m]=1;
	ROF(i,tp,1){
		if(i%2==1) continue;
		int now=i/2;//anss[now]=anss[now+1];
		if(suf[now+p[i]/2]){
			anss[now]++;
			ROF(j,top,now) suf[j]=1;
			top=now; 
		}
	}
	anss[m]=1;
	ll ans=0;
	FOR(i,1,m){
		ans+=(ansp[i]+1)*anss[i];
	}
	return ans;
}
int main(){
	freopen("paper.in","r",stdin);
	freopen("paper.out","w",stdout);
	n=read(),m=read();
	FOR(i,1,n){
		scanf("%s",s);
		int l=strlen(s);
		FOR(j,0,l-1){
			vec[i].push_back(s[j]-'a');
		}
	}
	ll ans=1;
	ans*=manacher1();
	FOR(i,1,n){
		FOR(j,0,vec[i].size()-1){
			vec2[j+1].push_back(vec[i][j]);
		}
	}
	swap(n,m);
	memset(pre,0,sizeof pre);
	memset(suf,0,sizeof suf);
	memset(ansp,0,sizeof ansp);
	memset(anss,0,sizeof anss);
	memset(p,0,sizeof p);tp=top=0;
	ans*=manacher2();
	printf("%lld",ans);
	return 0;
}

stone

博弈,完全不会系列。

首先找到最高的有奇数个 1 的位,显然我们只需要考虑这一位(如果不存在就是平局)。

那么现在序列中只有 0 和 1,取到奇数个 1 的获胜。

如果 n 是偶数,那么 Alice 显然可以取到下标为奇数的所有位置或者下标为偶数的所有位置,因此一定获胜。 (总有一个异或和是1)

如果 n 是奇数,如果 Alice 第一步取 0,那么 Bob 同理可以获胜(上面的情况),因此 Alice 第一步必须取 1。

如果两端都是 1 我们就枚举一个取走。同理,接下来 Alice 每一步必须和 Bob 上一步取走一样的数。 不难证明 Alice 能做到这一点当且仅当序列满足以下条件:先不断删去头尾相同的数剩下的数必须是(x1=x2,x3=x4,x5=x6,cdots)此时如果1的总个数为4k+1, 那么 Alice 就会取到奇数个 1,就能获胜。

原文地址:https://www.cnblogs.com/lcyfrog/p/13064528.html