福建省队集训 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)\)
这不就是对偶之后的标准形式了吗,所以我猜是对的
关于对偶:
变成
如果闲的无聊也可以把度数矩阵和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,就能获胜。