【题目】
原题地址
【解题思路】
个人认为这次noip最难的题目
以下来自这里
首先我们显然是要对一条斜线进行考虑,那么考虑现在什么情况才是合法的。
还有一种找规律的解法看这里
【参考代码】
状压
#include
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int n,m,len,ans,Am;
int t[40],f[2][40][(1<<8)+10];
int qpow(int x,int y)
{
int ret=1;
for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) ret=(ll)ret*x%mod;
return ret;
}
int get(int x,int y){return (x>>y-1)&1;}
int le(int x,int y){return len-x+1>=n?y-1:y;}
int ri(int x,int y){return len-x+1>=n?y:y+1;}
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int main()
{
#ifndef ONLINE_JUDGE
freopen("LGP5023.in","r",stdin);
freopen("LGP5023.out","w",stdout);
#endif
scanf("%d%d",&n,&m);Am=m;
if(n==1){printf("%d\n",qpow(2,m));return 0;}
if(m>n+1) m=n+1; len=n+m-1;
f[1][1][1]=f[1][0][1]=1;
for(int i=1;i<=n;++i) t[len-i+1]=min(i,m);
for(int i=2;i<=m;++i) t[len-(i+n-1)+1]=min(n,m-i+1);
for(int i=2;i<=len;++i)
{
int op=i&1;
for(int j=0;j<=n;++j) for(int k=0;k<1<<n;++k) f[op][j][k]=0;
for(int j=0;j<=t[i-1];++j) for(int k=0;k<1<<t[i-1];++k) if(f[op^1][j][k])
{
for(int l=0;l<=t[i];++l)
{
bool flag=1;
for(int p=1;p<t[i];++p) if(p^l && !get(k,ri(i,p))){flag=0;break;}
if(!flag) continue;
int S=0;
for(int p=1;p<=t[i];++p)
{
int kl=le(i,p),kr=ri(i,p);
if(kl<1 || kl>t[i-1]) {if(get(k,kr))S|=1<<p-1;}
else if(kr<1 || kr>t[i-1]) {if(get(k,kl))S|=1<<p-1;}
else if(get(k,kl) && get(k,kr) && j^kl) S|=1<<p-1;
}
up(f[op][l][S],f[op^1][j][k]);
}
}
}
for(int i=0;i<2;++i) for(int j=0;j<2;++j) up(ans,f[len&1][i][j]);
if(Am>m) ans=(ll)ans*qpow(3,Am-m)%mod;
printf("%d\n",ans);
return 0;
}
找规律(矩阵用于前50pt)
#include
using namespace std;
typedef long long ll;
const int mod=1e9+7,M=10;
int n,m,ans,op1,op2,op3,op4,S,Am,res;
int f[10];
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int qpow(int x,int y)
{
int ret=1;
for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) ret=(ll)ret*x%mod;
return ret;
}
struct Matrix
{
int a[M][M];
void clear(){memset(a,0,sizeof(a));}
void one(){clear();for(int i=0;i<S;++i)a[i][i]=1;}
}mat,Ans;
bool check(int x,int y)
{
for(int i=1;i<n;++i) if(((x>>i)&1)<((y>>(i-1))&1)) return 0;
return 1;
}
Matrix mul(Matrix const&x,Matrix const&y)
{
Matrix ret;ret.clear();
for(int i=0;i<S;++i) for(int j=0;j<S;++j) for(int k=0;k<S;++k)
up(ret.a[i][j],(ll)x.a[i][k]*y.a[k][j]%mod);
return ret;
}
Matrix qpow(Matrix x,int y)
{
Matrix ret;ret.one();
for(;y;y>>=1,x=mul(x,x)) if(y&1) ret=mul(ret,x);
return ret;
}
int solution1()
{
S=(1<<n);Am=m;m=min(m,n);
for(int i=0;i<S;++i) for(int j=0;j<S;++j) if(check(i,j)) mat.a[i][j]=1;
for(int i=0;i<S;++i) Ans.a[i][i]=1;
Ans=mul(Ans,qpow(mat,m-1));
for(int i=0;i<S;++i) for(int j=0;j<S;++j) up(res,Ans.a[i][j]);
if(n==3 && Am>=3) res=112;
res=(ll)res*qpow(3,Am-m)%mod;
printf("%d\n",res);
}
void solution2()
{
for(int i=1;i<=n-4;++i) f[i+1]=(ll)(4*5+f[i]*4)%mod;
ans=(ll)qpow(2,n)*(5*3+f[n-3]*2)%mod;
op1=(ll)qpow(4,n-2)*qpow(2,n-1)%mod*2*2%mod;
op2=(ll)qpow(4,n-4)*qpow(2,n-1)%mod*2*2*5%mod;
if(n==m) ans=(ll)(ans+op1+op2)%mod;
else
{
op3=(ll)(f[n-3]*3+4*5)*qpow(2,n)%mod;
op4=(ll)((f[n-3]*3+4*4)*qpow(2,n)%mod+4*3*qpow(2,n-1)%mod)%mod;
ans=(ll)((op1+op2)*3+op3+op4)%mod*qpow(3,m-n-1)%mod;
}
printf("%d\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("LGP5023.in","r",stdin);
freopen("LGP5023.out","w",stdout);
#endif
scanf("%d%d",&n,&m);
if(n<=3) solution1();
else solution2();
return 0;
}
【题目】
原题地址
【解题思路】
NOIP \text{NOIP} NOIP考的 ddp \text{ddp} ddp裸题。
首先考虑暴力的 dp \text{dp} dp,我们设 f u , 0 / 1 f_{u,0/1} fu,0/1表示这一位选或不选得到的最小花费。
如果当前点 u u u不选,那么它的所有儿子都要选,所以 f u , 0 = ∑ f v , 1 f_{u,0}=\sum f_{v,1} fu,0=∑fv,1
如果当前点 u u u选,那么它的儿子可以选也可以不选,所以 f u , 1 = w u + ∑ min { f v , 0 , f v , 1 } f_{u,1}=w_u+\sum \min \{ f_{v,0},f_{v,1}\} fu,1=wu+∑min{fv,0,fv,1}
现在要求支持独立的修改,那么考虑 ddp \text{ddp} ddp,我们需要将转移写成矩阵的形式。观察到转移方程是 ( min , + ) (\min,+) (min,+)的形式,所以是可以写出矩阵的,然后就是一个 ( min , + ) (\min,+) (min,+)的矩阵乘法。
由于要向上转移,我们还需要定义另一个 dp \text{dp} dp数组 g v , 0 / 1 g_{v,0/1} gv,0/1表示 v v v这个节点去掉后它父亲的 dp \text{dp} dp值。这个值我们用 f f f数组求一次和就可以得到。
我们现在将一个节点 v v v的 dp \text{dp} dp值看作一个矩阵,那么就是
[ f v , 0 f v , 1 ] \begin{bmatrix} f_{v,0}& f_{v,1} \end{bmatrix} [fv,0fv,1]
如果我们向上转移到 u u u的话,那么就是右乘转移矩阵
[ ∞ g v , 1 g v , 0 g v , 1 ] \begin{bmatrix} \infty &g_{v,1} \\ g_{v,0} & g_{v,1} \end{bmatrix} [∞gv,0gv,1gv,1]
以上是大概的思路。
对于这道题的询问,我们先按倍增求 l c a lca lca的套路向上跳, 跳的时候沿途乘转移矩阵。
如果这两个点原先不是祖先后代关系的话那么最后会跳到同一个节点的两个儿子
然后根据这两个儿子的新 dp \text{dp} dp值计算出它们 l c a lca lca的新 dp \text{dp} dp值 ,然后跳到根。
如果原先是祖先后代关系那么就直接把深的点向上跳到浅的点的位置,然后再跳到根即可。
总的复杂度就是 O ( ( n + q ) log n ) O((n+q)\log n) O((n+q)logn)
【参考代码】
#include
using namespace std;
typedef long long ll;
const ll INF=(ll)0x3f3f3f3f3f3f3f3f;
const int N=1e5+10;
int n,m,tot;
int head[N],Log[N],dep[N],fa[20][N],fc[20];
ll w[N],f[N][2];
char NOUSE[5];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
struct Tway{int v,nex;}e[N<<1];
void adde(int u,int v){e[++tot]=(Tway){v,head[u]};head[u]=tot;}
void addedge(int u,int v){adde(u,v);adde(v,u);}
void gmin(ll &x,ll y){x=min(x,y);}
ll up(ll x,ll y){return x+y>=INF?INF:x+y;}
struct Matrix
{
ll a[2][2];
void clear(){memset(a,0x3f3f,sizeof(a));}
void init(ll x,ll y){a[0][0]=INF;a[0][1]=a[1][1]=y;a[1][0]=x;}
Matrix operator * (Matrix const&x)
{
Matrix ret;ret.clear();
for(int i=0;i<2;++i) for(int j=0;j<2;++j) for(int k=0;k<2;++k)
gmin(ret.a[i][j],up(a[i][k],x.a[k][j]));
return ret;
}
}mat[20][N];
struct DP
{
ll a[2];
void clear(){memset(a,0x3f3f,sizeof(a));}
void init(ll x,ll y){a[0]=x;a[1]=y;}
};
DP mul(const DP&x,const Matrix&y)
{
DP ret;ret.clear();
for(int i=0;i<2;++i) for(int j=0;j<2;++j) gmin(ret.a[i],up(x.a[j],y.a[j][i]));
return ret;
}
void dfs1(int x)
{
f[x][1]=w[x];
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa[0][x]) continue;
fa[0][v]=x;dep[v]=dep[x]+1;dfs1(v);
f[x][0]+=f[v][1];f[x][1]+=min(f[v][0],f[v][1]);
}
}
void dfs2(int x)
{
for(int i=1;fc[i]<dep[x];++i)
fa[i][x]=fa[i-1][fa[i-1][x]],mat[i][x]=mat[i-1][x]*mat[i-1][fa[i-1][x]];
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa[0][x]) continue;
mat[0][v].init(f[x][0]-f[v][1],f[x][1]-min(f[v][0],f[v][1]));
dfs2(v);
}
}
ll work(int u,int x,int v,int y)
{
if(dep[u]<dep[v]) swap(u,v),swap(x,y);
//printf("%d %d %d %d\n",u,x,v,y);
DP t1,t2;t1.init(f[u][0],f[u][1]);t2.init(f[v][0],f[v][1]);
t1.a[x^1]=t2.a[y^1]=INF;
//printf("%lld %lld\n",t1.a[0],t1.a[1]);
for(int t=dep[u]-dep[v],i=0;i<19;++i)
if(t&fc[i]) t1=mul(t1,mat[i][u]),u=fa[i][u];
//printf("%lld %lld\n",t1.a[0],t1.a[1]);
int l;DP t;
if(u^v)
{
for(int i=18;~i;--i) if(fa[i][u]^fa[i][v])
{
t1=mul(t1,mat[i][u]);u=fa[i][u];
t2=mul(t2,mat[i][v]);v=fa[i][v];
}
l=fa[0][u];
t.init(up(f[l][0]-f[u][1]-f[v][1],up(t1.a[1],t2.a[1])),
up(f[l][1]-min(f[u][0],f[u][1])-min(f[v][0],f[v][1]),
up(min(t1.a[0],t1.a[1]),min(t2.a[0],t2.a[1]))));
}
else l=u,t=t1,t.a[y^1]=INF;
//printf("%lld %lld\n",t.a[0],t.a[1]);
for(int i=18;~i;--i) if(fa[i][l])
t=mul(t,mat[i][l]),l=fa[i][l];
ll ret=min(t.a[0],t.a[1]);
return ret>=INF?-1:ret;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("LGP5024.in","r",stdin);
freopen("LGP5024.out","w",stdout);
#endif
Log[1]=0;for(int i=1;i<N;++i) Log[i]=Log[i>>1]+1;
fc[0]=1;for(int i=1;i<19;++i) fc[i]=fc[i-1]<<1;
n=read();m=read();scanf("%s",NOUSE);
for(int i=1;i<=n;++i) w[i]=read();
for(int i=1;i<n;++i) addedge(read(),read());
dep[1]=1;dfs1(1);dfs2(1);
// for(int i=1;i<=n;++i,puts(""))
// for(int a=0;a<2;++a) for(int b=0;b<2;++b) printf("%lld ",mat[1][i].a[a][b]);
while(m--)
{
int a=read(),b=read(),c=read(),d=read();
printf("%lld\n",work(a,b,c,d));
}
return 0;
}