正题
cometoj出的题质量还是蛮高的,至少让我这种菜鸡选手找到了思考的乐趣。
T1
这题很水,因为要找波浪,所以用a1,a2分别记录以i结尾的降波浪和升波浪的种数,每次交换一下把其中的一边清空为1就可以了。
#include<bits/stdc++.h>
using namespace std;
long long ans=0;
int a1,a2,n,las,x;
int main(){
scanf("%d",&n);
scanf("%d",&las);
a1=a2=1;
for(int i=2;i<=n;i++){
scanf("%d",&x);
if(las<=x) a2=a1+1,a1=1;
else a1=a2+1,a2=1;
ans+=a1+a2-2;las=x;
}
printf("%lld",ans);
}
T2
一眼下去直接写了一个扩展欧几里得找非负整数解的个数,看了看题发现不对,这个还有顺序,想着构造多项式想了半天,不太实际。
发现有一个的矩阵快速幂做法,但是这个是齐次线性递推式,所以可以用特征多项式做到,然后有一个的做法,就是把的倍数提出来递推。又有一个的做法,这个就是直接把ax+by=n的所有解(x,y)找出来,每一组解的贡献就是,考虑到x+y可能很大,但是模数很小,所以直接上Lucas.
那么把做法拼在一起,就可以拿到全部的分数。
比赛的时候忘记判无解少了30分,OJ还炸了。
#include<bits/stdc++.h>
using namespace std;
const int N=110;
long long n,a,b;
int T;
const long long mod=10007;
struct Matrix{
int g[N][N];
friend Matrix operator*(const Matrix&A,const Matrix&B){
Matrix G;
for(int i=1;i<=T;i++)
for(int j=1;j<=T;j++){
G.g[i][j]=0;
for(int k=1;k<=T;k++)
G.g[i][j]+=A.g[i][k]*B.g[k][j]%mod;
G.g[i][j]%=mod;
}
return G;
}
}TOT,X;
int fac[mod],inv[mod];
int ksm(int x,int t){
int tot=1;
while(t){
if(t&1) (tot*=x)%=mod;
(x*=x)%=mod;t/=2;
}
return tot;
}
void solve1(){
T=max(a,b);
for(int i=1;i<=T;i++) TOT.g[i][i]=1;
X.g[a][1]++,X.g[b][1]++;
for(int i=1;i<=T-1;i++) X.g[i][i+1]=1;
while(n){
if(n&1) TOT=TOT*X;
X=X*X;n/=2;
}
printf("%d\n",TOT.g[1][1]);
}
int exgcd(int a,int b,long long&x,long long&y){
if(b==0) {x=1;y=0;return a;}
int z=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return z;
}
int C(int x,int y){
return (x<y?0:fac[x]*inv[y]%mod*inv[x-y]%mod)%mod;
}
int Lucas(int x,int y){
if(x==0 && y==0) return 1;
return Lucas(x/mod,y/mod)*C(x%mod,y%mod)%mod;
}
void solve2(){
long long x,y;
int g=exgcd(a,b,x,y);
x*=n/g,y*=n/g;
if(n%g) {printf("0");return ;}
long long La=a/g,Lb=b/g,tmp;
if(x<0) tmp=(-x+Lb-1)/Lb,x+=tmp*Lb,y-=tmp*La;
if(y<0) tmp=(-y+La-1)/La,y+=tmp*La,x-=tmp*Lb;
if(x<0 || y<0) {printf("0");return ;}
tmp=x/Lb,x-=tmp*Lb,y+=tmp*La;
int ans=0;
while(y>=0){
ans+=Lucas(x+y,x);ans%=mod;
x+=Lb,y-=La;
}
printf("%d\n",ans);
}
int main(){
fac[0]=1;for(int i=1;i<mod;i++) fac[i]=fac[i-1]*i%mod;
inv[mod-1]=ksm(fac[mod-1],mod-2);for(int i=mod-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
scanf("%lld %lld %lld",&n,&a,&b);
if(a<=100 && b<=100) solve1();
else solve2();
}
T3
第三题是道好题,结论易证但是并不显然:联通块个数=n-两端相同颜色边数目+所有点颜色相同的简单环个数。
证明可以首先不考虑简单环,这个是显然正确的。
考虑给一个联通块加上一个颜色相同的简单环(假设其中有一个点已经在里面),那么边的数量等于加上的点的数量+1,所以减的就多了1,加回来即可。
显然不存在其他情况,因此得证。
接着的事情就很简单了,因为这个是一个点不可能存在于多个简单环的仙人掌,所以跑Tarjan条件就少了很多,另外考虑到一个点最多有两个父亲,所以直接记录下来,另外记录一下自己儿子取某种颜色的总概率,每一次O(k)更新就可以了,环的情况是相乘的,所以0的情况特殊处理一下就可以了,另外不要因为贪快而省掉许多mod导致错误哦!
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const long long mod=998244353;
int n,m,k,q;
struct edge{
int y,next;
}s[N<<2];
int first[N],len=0,fa1[N],fa2[N],t[N][5];
int T=0,B=0;
long long f[N][5],W[N][5],F[N][5],ans;
int sta[N],top=0,dfn[N],low[N],where[N];
void ins(int x,int y){
s[++len]=(edge){y,first[x]};first[x]=len;
}
long long ksm(long long x,long long t){
long long tot=1;
while(t){
if(t&1) (tot*=x)%=mod;
(x*=x)%=mod;t/=2;
}
return tot;
}
void init(int x){
static int T=0;T=0;
for(int j=0;j<k;j++) scanf("%d",&f[x][j]),T+=f[x][j];
T=ksm(T,mod-2);for(int j=0;j<k;j++) f[x][j]=f[x][j]*T%mod;
}
void Tarjan(int x,int fa){
low[x]=dfn[x]=++T;sta[++top]=x;
for(int i=first[x];i!=0;i=s[i].next) if(s[i].y!=fa){
if(dfn[s[i].y]) low[x]=min(low[x],dfn[s[i].y]);
else Tarjan(s[i].y,x),low[x]=min(low[x],low[s[i].y]);
}
if(low[x]==dfn[x]){
B++;
for(int j=0;j<k;j++) W[B][j]=1;
int P=0;
while(1){
int now=sta[top];top--;
for(int j=0;j<k;j++) f[now][j]?W[B][j]=W[B][j]*f[now][j]%mod:t[B][j]++;
where[now]=B;P++;
if(now==x) break;
}
if(P==1) where[x]=0,B--;
}
}
void dfs(int x){
dfn[x]=true;
for(int i=first[x];i!=0;i=s[i].next) if(s[i].y!=fa1[x]){
if(dfn[s[i].y] && fa2[s[i].y]!=x){
fa2[x]=s[i].y;
for(int j=0;j<k;j++) F[s[i].y][j]+=f[x][j];
}
else if(!dfn[s[i].y]){
for(int j=0;j<k;j++) F[x][j]+=f[s[i].y][j];
fa1[s[i].y]=x;dfs(s[i].y);
}
}
}
int main(){
scanf("%d %d %d",&n,&m,&k);
int type,x,y;
for(int i=1;i<=m;i++) scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
for(int i=1;i<=n;i++) init(i);
Tarjan(1,0);
memset(dfn,0,sizeof(dfn));
dfs(1);
scanf("%d",&q);
ans=n;
for(int i=1;i<=n;i++) for(int j=0;j<k;j++) F[i][j]%=mod,ans+=mod-F[i][j]*f[i][j]%mod;
for(int i=1;i<=B;i++) for(int j=0;j<k;j++) ans+=(t[i][j]?0:W[i][j]);ans%=mod;
while(q--){
scanf("%d",&type);
if(type==1){
scanf("%d",&x);
if(fa1[x]) for(int j=0;j<k;j++) ans+=f[x][j]*f[fa1[x]][j]%mod,F[fa1[x]][j]+=mod-f[x][j];
if(fa2[x]) for(int j=0;j<k;j++) ans+=f[x][j]*f[fa2[x]][j]%mod,F[fa2[x]][j]+=mod-f[x][j];
for(int j=0;j<k;j++) ans+=f[x][j]*F[x][j]%mod;
if(where[x]) for(int j=0;j<k;j++) ans+=mod-(t[where[x]][j]?0:W[where[x]][j]),f[x][j]?(W[where[x]][j]*=ksm(f[x][j],mod-2))%=mod:t[where[x]][j]--;
init(x);
if(fa1[x]) for(int j=0;j<k;j++) ans+=mod-f[x][j]*f[fa1[x]][j]%mod,(F[fa1[x]][j]+=f[x][j])%=mod;
if(fa2[x]) for(int j=0;j<k;j++) ans+=mod-f[x][j]*f[fa2[x]][j]%mod,(F[fa2[x]][j]+=f[x][j])%=mod;
for(int j=0;j<k;j++) ans+=mod-f[x][j]*F[x][j]%mod;
if(where[x]) for(int j=0;j<k;j++) (f[x][j]?(W[where[x]][j]*=f[x][j])%=mod:t[where[x]][j]++),ans+=(t[where[x]][j]?0:W[where[x]][j]);
ans%=mod;
}
else printf("%lld\n",ans);
}
}