福建省队集训 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\right)\)

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

关于对偶:

\[\operatorname{minimize} \sum_ip_i+\sum_ih_i\\ \operatorname{s.t} \left\{\begin{array}{l}p_i+h_j\geq w_{i,j}\\ p_i,h_i\geq 0 \end{array}\right. \]

变成

\[\begin{array}{c} \operatorname{maximize} \sum_{(u, v)} w_{u v} d_{u v} \\ \text {s.t}\left\{\begin{array}{l} \sum_{v} d_{u v} \leq 1, u \in X \\ \sum_{v} d_{u v} \leq 1, v \in Y \end{array}\right. \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,就能获胜。

posted @ 2020-06-08 11:14  lcyfrog  阅读(125)  评论(0编辑  收藏  举报