总结:难度<=noip,不能ak说明自己对卡常的掌控不够熟练。
A. 【18 省选 3】树的染色
不难发现因为限定了每个子树中和根同色的总权值为vi,所以可以贪心地使另一种颜色总权值最小,背包即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 10000005 4 #define N 5005 5 int ff,fa[N],f[1005][N],g[N],p[N],q[N],v[N]; 6 vector<int>G[N]; 7 void dfs(int x) 8 { 9 if(G[x].size()==0){p[x]=v[x];q[x]=0;return;} 10 for(int i=0;i<=v[x];i++)f[x][i]=0; 11 for(int i=0;i<G[x].size();i++) 12 { 13 int y=G[x][i];dfs(y); 14 for(int j=0;j<=v[x];j++)g[j]=inf; 15 for(int j=0;j<=v[x];j++)if(j>=p[y])g[j]=min(g[j],f[x][j-p[y]]+q[y]); 16 for(int j=0;j<=v[x];j++)if(j>=q[y])g[j]=min(g[j],f[x][j-q[y]]+p[y]); 17 for(int j=0;j<=v[x];j++)f[x][j]=g[j]; 18 } 19 p[x]=v[x];q[x]=inf; 20 for(int i=0;i<=v[x];i++)q[x]=min(q[x],f[x][i]); 21 if(q[x]>=inf)ff=0; 22 } 23 int main() 24 { 25 int T;scanf("%d",&T); 26 while(T--) 27 { 28 int n;scanf("%d",&n); 29 for(int i=1;i<=n;i++)G[i].clear(); 30 for(int i=2;i<=n;i++)scanf("%d",&fa[i]),G[fa[i]].push_back(i); 31 for(int i=1;i<=n;i++)scanf("%d",&v[i]); 32 ff=1;dfs(1); 33 if(ff)puts("POSSIBLE");else puts("IMPOSSIBLE"); 34 } 35 return 0; 36 }
B. 【18 省选 3】树形图求和
有向图的生成树计数可以用出度矩阵-邻接矩阵去掉根所在行列的矩阵A的行列式得到,此题可以考虑先求出所有生成树个数S,每次枚举一条边权为w,去掉w对矩阵的影响求生成树个数s,贡献是(S-s)*w,
考虑如何快速计算S-s,用行列式的计算方法我们可以枚举w(u,v)所在的行u展开,S-s等于T(u,u)-T(u,v)其中T(x,y)=表示(-1)x+yM(x,y),M(x,y)是行列式去掉行x列y的余子式,相当于需要快速计算T矩阵。T的转置A'是矩阵A的伴随矩阵,可以由定理A*A'=|A|*E快速求出,只需要高斯消元实现行列式求值和矩阵求逆即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=305,M=1e5+5,mod=1e9+7; 4 int n,m,ans,u[M],v[M],w[M],mat[N][N],t[N][N],t1[N][N],t2[N][N]; 5 int pw(int a,int b){int r=1;for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)r=1ll*r*a%mod;return r;} 6 int det() 7 { 8 int d=1; 9 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)t[i][j]=mat[i][j]; 10 for(int i=1;i<n;i++) 11 { 12 for(int j=i;j<n;j++)if(mat[j][i]) 13 { 14 swap(mat[i],mat[j]); 15 if(i!=j)d=1ll*d*(mod-1)%mod; 16 break; 17 } 18 int iv=pw(mat[i][i],mod-2); 19 for(int j=i+1;j<n;j++) 20 { 21 int c=1ll*mat[j][i]*iv%mod; 22 for(int k=i;k<n;k++)mat[j][k]=(mat[j][k]-1ll*c*mat[i][k]%mod+mod)%mod; 23 } 24 d=1ll*d*mat[i][i]%mod; 25 } 26 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)mat[i][j]=t[i][j]; 27 return d; 28 } 29 void pre() 30 { 31 for(int i=1;i<n;i++)t2[i][i]=1; 32 for(int i=1;i<n;i++)for(int j=1;j<n;j++)t1[i][j]=mat[j][i]; 33 for(int i=1;i<n;i++) 34 { 35 for(int j=i;j<n;j++)if(t1[j][i]){swap(t1[i],t1[j]);swap(t2[i],t2[j]);break;} 36 int iv=pw(t1[i][i],mod-2); 37 for(int j=1;j<n;j++)t1[i][j]=1ll*t1[i][j]*iv%mod,t2[i][j]=1ll*t2[i][j]*iv%mod; 38 for(int j=1;j<n;j++)if(j!=i) 39 { 40 int c=t1[j][i]; 41 for(int k=1;k<n;k++)t1[j][k]=(t1[j][k]-1ll*c*t1[i][k]%mod+mod)%mod,t2[j][k]=(t2[j][k]-1ll*c*t2[i][k]%mod+mod)%mod; 42 } 43 } 44 } 45 int main() 46 { 47 scanf("%d%d",&n,&m); 48 for(int i=1;i<=m;i++) 49 { 50 scanf("%d%d%d",&u[i],&v[i],&w[i]); 51 mat[u[i]][u[i]]++;mat[u[i]][v[i]]--; 52 } 53 pre();int r=det(),ans=0; 54 for(int i=1;i<=m;i++)if(u[i]<n) 55 { 56 int x=(t2[u[i]][u[i]]-t2[u[i]][v[i]]+mod)%mod; 57 (ans+=1ll*r*x%mod*w[i]%mod)%=mod; 58 } 59 printf("%d\n",ans); 60 return 0; 61 }
C. 【18 省选 3】波波老师
注意常数:线性被卡惨案。由于字符集大小为5,所以考虑SAM的线性做法,套上单调队列即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define N 10000005 5 #define inf 1000000007 6 int n,cnt,lst,len[N],lk[N],ch[N][5],id[N>>1],f[N>>1],q[N>>1],dp[N>>1]; 7 char s[N>>1];bool ff[N];ll res; 8 int extend(int c) 9 { 10 int p=lst,np=lst=++cnt;len[np]=len[p]+1; 11 for(;p&&!ch[p][c];p=lk[p])ch[p][c]=np; 12 if(!p)lk[np]=1; 13 else 14 { 15 int q=ch[p][c],nq; 16 if(len[q]==len[p]+1)lk[np]=q; 17 else 18 { 19 nq=++cnt;len[nq]=len[p]+1;lk[nq]=lk[q];lk[q]=lk[np]=nq; 20 memcpy(ch[nq],ch[q],sizeof(ch[q])); 21 for(;p&&ch[p][c]==q;p=lk[p])ch[p][c]=nq; 22 } 23 } 24 return lst; 25 } 26 int main() 27 { 28 scanf("%s",s);n=strlen(s);lst=cnt=1; 29 for(int i=n-1;i>=0;i--)id[i]=extend(s[i]-'a'); 30 for(int i=2;i<=cnt;i++)ff[lk[i]]=1; 31 for(int i=0;i<n;i++)if(ff[id[i]])f[i]=inf;else f[i]=len[lk[id[i]]]+1; 32 for(int i=0;i<n;i++)dp[i]=inf; 33 for(int i=0,l=1,r=0;i<n;i++) 34 { 35 for(;l<=r&&f[q[r]]>f[i];r--); 36 q[++r]=i; 37 for(;l<=r&&q[l]+f[q[l]]<=i;l++); 38 dp[i]=f[q[l]]; 39 } 40 for(int i=1;i<n;i++)dp[i]=min(dp[i],dp[i-1]+1); 41 for(int i=0;i<n;i++)res+=dp[i]; 42 printf("%lld\n",res); 43 return 0; 44 }