http://opentrains.snarknews.info/~ejudge/team.cgi?contest_id=001492
A. Even Three is Odd
暴力 dp 到第 (i) 位时要记录 (i-1) 和 (i-2) 的值,但是发现答案只和 (max) 有关,于是可以改为记录 (i-1) 和 (i-2) 的最大值以及位置,这样状态数是 (O(n^2)) 的。
注意细节,用前缀和优化即可做到 (O(1)) 转移。
#include<bits/stdc++.h>
#define P 1000000007
#define N 2005
inline int fmo(int x){
return x+((x>>31)&P);
}
int n,a[N];
int f[2][N],s[N],t[N],r[N],g[2][N];
int main(){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
f[1][i]=1,f[0][i]=i;
for(int i=3;i<=n+1;i++){
for(int k=1;k<=n;k++){
s[k]=fmo(s[k-1]+f[0][k]-P);
t[k]=fmo(t[k-1]+1ll*(k-1)*f[1][k]%P-P);
r[k]=fmo(r[k-1]+1ll*a[k]*f[1][k]%P-P);
}
if(i>n)
break;
for(int k=1;k<=n;k++){
g[0][k]=fmo(1ll*a[k]*fmo(s[k]+t[k]-P)%P+1ll*k*fmo(r[n]-r[k])%P-P);
g[1][k]=fmo(1ll*a[k]*f[0][k]%P+fmo(r[n]-r[k])-P);
}
std::swap(f,g);
}
printf("%d
",fmo(s[n]+t[n]-P));
}
}
C. City United
发现可以转化为对诱导子图的连通块黑白染色的方案数 (mod 4) 。
于是直接在原图上 (O(n3^{13})) 状压即可。
#include<bits/stdc++.h>
#define N 55
#define L 1594323
int n,m;
int E[N],a[L],b[L];
int pw[13],f[L],g[L];
int ans;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
if(u<v)
std::swap(u,v);
E[u]|=1<<(u-v-1);
}
pw[0]=1;
for(int i=1;i<13;i++)
pw[i]=pw[i-1]*3;
for(int i=0;i<L;i++)
for(int j=0;j<13;j++)
if(i/pw[j]%3==1)
a[i]|=1<<j;
else if(i/pw[j]%3==2)
b[i]|=1<<j;
f[0]=1;
for(int u=1;u<=n;u++){
for(int s=0;s<L;s++)
g[s]=0;
for(int s=0;s<L;s++){
int t=s*3%L;
(g[t]+=f[s])&=3;
if(!(b[s]&E[u]))
(g[t+1]+=f[s])&=3;
if(!(a[s]&E[u]))
(g[t+2]+=f[s])&=3;
}
std::swap(f,g);
}
for(int i=0;i<L;i++)
(ans+=f[i])&=3;
puts(ans==1?"0":"1");
}
D. Coins 2
记 (m=operatorname{lcm}(1,2,ldots,n)),(s=sum_{i=1}^n ia_i)。
首先对于 (s<2nm) 可以暴力背包。
否则,对于 (xgeq nm) 能被凑出,一定存在 (i) 使得它的出现次数 (geq m/i),所以 (x-m) 能被凑出。由对称性,对于 (xleq s-nm) 能被凑出,(x+m) 能被凑出。
于是对于 (xin[(n-1)m,s-nm]) 能被凑出当且仅当 (x+m) 能被凑出。所以只用背包求出 ([0,nm)) 是否能被凑出即可。
复杂度 (O(n^2m))。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define N 20
int n,m,a[N];
ll s;
ll ans;
int f[10810800];
int main(){
while(~scanf("%d",&n)){
s=0,m=1;
for(int i=1;i<=n;i++){
m=m*i/std::__gcd(m,i);
scanf("%d",&a[i]);
s+=1ll*i*a[i];
}
for(int i=0;i<2*n*m;i++)
f[i]=inf;
f[0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<2*n*m;j++)
if(j>=i&&f[j-i]<a[i])
f[j]=std::min(f[j],f[j-i]+1);
for(int j=0;j<2*n*m;j++)
f[j]=f[j]<=a[i]?0:inf;
}
ans=0;
if(s<2*n*m){
for(int i=0;i<=s;i++)
ans+=!f[i];
}
else{
for(int i=0;i<n*m;i++)
ans+=!f[i];
ans<<=1;
for(int i=0;i<m;i++)
ans+=(!f[(n-1)*m+i])*(s-n*m-(n-1)*m-i)/m;
}
printf("%lld
",ans);
}
}
F. Multi-stage Marathon
记 (v) 为到所有点的期望人数的向量,那么要求的就是每个时刻的 (v_n)。
发现除了至多 (m) 个时刻之外,别的时刻的 (v) 都是前一个时刻的 (v) 乘上一个固定的矩阵 (M)。
注意到求矩阵乘向量的某一项是 (O(n)) 的,所以可以预处理一下 (M,M^2,ldots,M^B),对每 (B) 个位置直接计算 ((M^iv)_n) 然后重新更新一下 (v)。
复杂度为 (O(n^3B+n^2{Tover B}+Tn+mn^2)),取 (B=sqrt{T/n}) 应该就能过了。
代码咕了。
G. Matrix Recurrence
好像是一个叫 baka-trick 的东西。
用两个栈底相对的栈模拟队列,维护左栈的后缀积以及右栈的积,当左栈弹空时暴力把右栈倒过来当作新的左战然后开一个空栈作为新的右栈即可,这样是均摊线性的。
#include<bits/stdc++.h>
#define N 1000005
int P;
inline int fmo(int x){
return x+((x>>31)&P);
}
int n,m;
struct node{
int a[5][5];
node(int tp=0){
memset(a,0,sizeof(a));
for(int i=0;i<m;i++)
a[i][i]=tp;
}
};
node operator * (node x,node y){
node res;
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
res.a[i][j]=fmo(res.a[i][j]+1ll*x.a[i][k]*y.a[k][j]%P-P);
return res;
}
node A[N],B,s[N],C;
int main(){
while(~scanf("%d%d%d",&n,&m,&P)){
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
scanf("%d",&A[0].a[i][j]);
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
scanf("%d",&B.a[i][j]);
s[0]=A[0],C=node(1);
for(int mid=0,r=1;r<=n;r++){
int x;
scanf("%d",&x);
if(x>mid){
s[r-1]=A[r-1];
for(int i=r-2;i>mid;i--)
s[i]=A[i]*s[i+1];
mid=r-1,C=node(1);
}
A[r]=s[x]*C*B;
C=C*A[r];
}
for(int i=0;i<m;i++){
for(int j=0;j<m;j++)
printf("%d ",A[n].a[i][j]);
puts("");
}
}
}
K
考虑如何判定一个方案是否合法,一个贪心的方法是选择一个最大的前缀让它们从 EC-Final 出线,剩下的用区域赛出线。
于是考虑枚举 (k),硬点 EC-Final 的前 (k) 名直接出线,第 (k+1) 名不出线,求后面的人通过区域赛出线的集合数.
发现这只和每个队在区域赛的最高排名有关,用树状数组统计下就好了。
#include<bits/stdc++.h>
#define P 1000000007
#define N 200005
inline int fmo(int x){
return x+((x>>31)&P);
}
int pw[N];
int n,m;
int a[N],c[N];
int t[N];
inline int lb(int x){
return x&-x;
}
inline void add(int x,int d){
for(;x<=m;x+=lb(x))
t[x]=fmo(t[x]+d-P);
}
inline int sum(int x){
int res=0;
for(;x;x-=lb(x))
res=fmo(res+t[x]-P);
return res;
}
int ans;
int main(){
pw[0]=1;
for(int i=1;i<=2e5;i++)
pw[i]=fmo(pw[i-1]+pw[i-1]-P);
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=m;i++)
a[i]=m+1;
for(int i=1;i<=n;i++){
int k;
scanf("%d",&k);
for(int j=1;j<=k;j++){
int x;
scanf("%d",&x);
a[x]=std::min(a[x],j);
}
}
for(int i=1;i<=m;i++)
t[i]=c[i]=0;
for(int i=1;i<=m;i++)
c[a[i]]++;
for(int i=1;i<=m;i++)
add(i,fmo(pw[c[i]]-1));
ans=1;
for(int i=1;i<=m;i++){
int x;
scanf("%d",&x);
add(a[x],fmo(1-pw[c[a[x]]]));
c[a[x]]--;
add(a[x],fmo(pw[c[a[x]]]-1));
ans=fmo(ans+sum(std::min(m,a[x]))+1-P);
}
printf("%d
",ans);
}
}