小总结:
昨天晚上调一道题题解总结又咕咕了......(虽然我也知道没几个人看)。
但是还是要补的,毕竟我想留下完整的第一次集训的一些东西qwq.....
昨天我考的是我集训以来最差的一次,险些都要掉到20开外了......结果一天都感觉用不上力气,一道绿题让lc帮我调了将近一个小时......
T1:食物链
刚刚看到这道题还以为是之前考过的一道并查集的题目,但是读完题目之后才知道完全不一样......
做这道题之前我们要知道食物链的概念是一条链,不是一个环,于是我们考虑建好图之后从入度为0的点出发进行dfs,每到达一个出度为0的点之后就记一次答案,但是题目中有提到单单一个点不是一条食物链,但是我但是考虑过度了...明明我的dfs不用考虑但是之后我还是去了一次这些点,结果70->20。之后会T,想要拿到满分需要记忆化优化
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 const int N=1e6+10; 5 int n,m; 6 struct Node{ 7 int next,to; 8 }edge[N]; 9 int Head[N],tot; 10 void Add(int x,int y){ 11 edge[++tot].to=y; 12 edge[tot].next=Head[x]; 13 Head[x]=tot; 14 } 15 int dp[N],ru[N],chu[N],ans; 16 int dfs(int u){ 17 if(dp[u]) return dp[u]; 18 int ans=0; 19 if(!chu[u]&&ru[u]) ans++; 20 for(int i=Head[u];i;i=edge[i].next){ 21 int v=edge[i].to; 22 ans+=dfs(v); 23 } 24 dp[u]=ans; 25 return ans; 26 } 27 signed main(){ 28 scanf("%lld%lld",&n,&m); 29 for(int i=1;i<=m;++i){ 30 int x,y; 31 scanf("%lld%lld",&x,&y); 32 Add(x,y); 33 chu[x]++;ru[y]++; 34 } 35 for(int i=1;i<=n;++i){ 36 if(!ru[i]){ 37 ans+=dfs(i); 38 } 39 } 40 printf("%lld ",ans); 41 return 0; 42 }
T2:升降梯上
(鬼知道这是一道最短路呀)
最初我看到这道题的时候想到的是dp,当然我也写出来了,定义dp[i][j]表示当前在i层,上一次扳到j时的时间花费,但是时间效率是n^2*m^2的,T到了只剩下50pts.......
正解是最短路,把某一层的某一个扳的位置映射在一条直线上,i层第j个的位置为i*m+j,之后把每层可以扳到的位置建一条以时间为长度的边,之后把该层可以跳到的层之间进行连一条以时间为长度的边,最后从起始的点找一条到i层某一状态的最短路就可以了,千万注意边不要连到1~n之外.
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e6+10; 4 struct Node{ 5 int next,to,dis; 6 }edge[N]; 7 int a[N],Head[N],tot,m,n; 8 void Add(int x,int y,int z){ 9 edge[++tot].to=y; 10 edge[tot].dis=z; 11 edge[tot].next=Head[x]; 12 Head[x]=tot; 13 } 14 int dis[N],vis[N],begin; 15 struct Edge{ 16 int num,dis; 17 Edge(int x,int y){ 18 num=x;dis=y; 19 } 20 bool operator < (const Edge &a)const{ 21 return a.dis<dis; 22 } 23 }; 24 priority_queue<Edge>q; 25 void dijkstra(int x){ 26 memset(dis,0x3f,sizeof(dis)); 27 memset(vis,0,sizeof(vis)); 28 dis[x]=0;q.push(Edge(x,0)); 29 while(!q.empty()){ 30 int u=q.top().num;q.pop(); 31 if(vis[u]) continue; 32 vis[u]=1; 33 for(int i=Head[u];i;i=edge[i].next){ 34 int v=edge[i].to; 35 if(dis[v]>dis[u]+edge[i].dis){ 36 dis[v]=dis[u]+edge[i].dis; 37 q.push(Edge(v,dis[v])); 38 } 39 } 40 } 41 } 42 int change(int x,int j){ 43 return x*m+j; 44 } 45 int main(){ 46 scanf("%d%d",&n,&m); 47 for(int i=1;i<=m;++i){ 48 scanf("%d",&a[i]); 49 if(a[i]==0){ 50 begin=change(1,i); 51 } 52 } 53 for(int i=1;i<n;++i) 54 for(int j=1;j<=m;++j){ 55 if(i+a[j]>0&&i+a[j]<=n&&a[j]!=0){ 56 Add(change(i,j),change(i+a[j],j),2*abs(a[j])); 57 } 58 for(int k=1;k<=m;++k){ 59 if(k!=j){ 60 Add(change(i,j),change(i,k),abs(k-j)); 61 } 62 } 63 } 64 dijkstra(begin); 65 int ans=2147483647; 66 for(int i=1;i<=m;++i) ans=min(ans,dis[change(n,i)]); 67 if(ans!=0x3f3f3f3f) printf("%d ",ans); 68 else printf("-1 "); 69 return 0; 70 }
T3:Password
前天虎哥说会有一道数论的题目,还重点提了一下拓展欧几里德,我其他的几乎没怎么看,谁知道老师考了一道矩阵乘法和欧拉定理的题目......
考虑题目要求的答案是pfibo(p)%q的答案,既然p是质数,q还小于p,于是p,q一定互质,我们根据欧拉定理可以知道pφ(q)≡1(mol q),于是我们可以把fibo(p)拆为x*φ(q)+t,前面的φ(q)次方都是一,对答案没有贡献,直接减去,最后我们只需要求出pt % q的值就可以了,减去φ(q)的过程我们可以用矩阵乘法求fibo函数的过程中对该数取模,在得到t之后进行一个倍增快速幂就可以了,注意取模的个数,%的太多也不好,会T掉,之后找gyz大佬才调好......
1 #include<bits/stdc++.h> 2 #define debug printf("-debug- ") 3 #define int long long 4 using namespace std; 5 const int N=5e6+10; 6 inline int read(){ 7 int x=0,f=1; 8 char ch=getchar(); 9 while(ch<'0'||ch>'9'){ 10 if(ch=='-') 11 f=-1; 12 ch=getchar(); 13 } 14 while(ch>='0'&&ch<='9'){ 15 x=(x<<1)+(x<<3)+(ch^48); 16 ch=getchar(); 17 } 18 return x*f; 19 } 20 struct squ{ 21 int a[3][3]; 22 }; 23 int Mod; 24 squ multiply(squ a,squ b){ 25 squ ans; 26 memset(ans.a,0,sizeof(ans.a)); 27 for(int i=1;i<=2;++i) 28 for(int j=1;j<=2;++j) 29 for(int k=1;k<=2;++k){ 30 ans.a[i][j]=(ans.a[i][j]%Mod+a.a[i][k]*b.a[k][j]%Mod)%Mod; 31 } 32 return ans; 33 } 34 void cal(squ &a,squ b,int cnt){ 35 while(cnt){ 36 if(cnt & 1){ 37 a=multiply(a,b); 38 } 39 cnt>>=1;b=multiply(b,b); 40 } 41 } 42 squ fibo,origin; 43 int getphi(int x){ 44 int ans=x; 45 for(int i=2;i<=sqrt(x+0.5);++i){ 46 if(x%i==0){ 47 ans=ans/i*(i-1);while(x%i==0) x/=i; 48 } 49 50 } 51 if(x!=1) ans=ans/x*(x-1); 52 return ans; 53 } 54 int Pow(int x,int cnt,int M){ 55 int ans=1; 56 while(cnt){ 57 if(cnt & 1) ans=ans*x%M; 58 cnt>>=1;x=x*x%M; 59 } 60 return ans%M; 61 } 62 signed main(){ 63 int T=read(),p=read(); 64 while(T--){ 65 origin.a[1][1]=1;origin.a[1][2]=1; 66 origin.a[2][1]=1;origin.a[2][2]=0; 67 fibo.a[1][1]=fibo.a[1][2]=1; 68 fibo.a[2][1]=fibo.a[2][2]=0; 69 int x=read(),y=read(); 70 Mod=getphi(y); 71 if(x>2) 72 cal(fibo,origin,x-2); 73 //printf("%d ",fibo.a[1][1]); 74 printf("%lld ",Pow(p,fibo.a[1][1],y)); 75 } 76 return 0; 77 }
T4:子串
考场上没能想出正解的思路......看数据范围打了一个30的基础分就没有再看了......
这道题卡内存,于是我们在定义好状态之后要考虑优化,定义dp[i][j][k][p]表示a串的第i个位置为止使用k个子串匹配b串前j位字符的状态,最后一维表示当前状态该数选不选,如果当前我们不选这位数字,那么存在转移dp[i][j][k][0]=dp[i-1][j][k][1]+dp[i-1][j][k][0],因为既然前面不选,当前的状态就与前面无关了,于是都加上,如果要选,要分情况讨论,如果a[i]==b[j],那么dp[i][j][k][1]=dp[i-1][j-1][k][1](在上一位基础上续上)+dp[i-1][j-1][k-1][0](前面断了,这是第k段的开端)+dp[i-1][j-1][k-1][1](令起炉灶不管前面自成一段),如果a[i]!=a[j],由于我们选不了,dp[i][j][k][1]就是0啦.我们注意到当前转移只与上一位有关系,于是可以用滚动数组压掉一位,之后要注意每次跑完一位要清空另一位,因为滚动的话我们之前的计算结果不清空可能会对之后的结果造成影响.这样就解决了.
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e6+10; 4 char a[2001],b[201]; 5 int n,m,t,f[2][201][201][2],s,k; 6 const int Mod=1000000007; 7 int main(){ 8 scanf("%d%d%d",&n,&m,&t); 9 scanf("%s%s",a+1,b+1); 10 for(int i=1;i<=n;++i){ 11 k=!k; 12 f[k][1][1][0]=s; 13 if(a[i]==b[1]){ 14 f[k][1][1][1]=1;s++; 15 } 16 for(int j=2;j<=m;++j) 17 for(int p=1;p<=t;++p){ 18 if(a[i]==b[j]) 19 f[k][j][p][1]=((f[!k][j-1][p-1][1]+f[!k][j-1][p][1])%Mod+f[!k][j-1][p-1][0])%Mod; 20 f[k][j][p][0]=(f[!k][j][p][0]+f[!k][j][p][1])%Mod; 21 } 22 for(int j=1;j<=m;++j) 23 for(int p=1;p<=t;++p) 24 f[!k][j][p][1]=f[!k][j][p][0]=0; 25 26 } 27 printf("%d ",(f[k][m][t][1]+f[k][m][t][0])%Mod); 28 return 0; 29 }
一道单调队列的基础题目
昨天(或是前天???)的模拟赛之中的第一题被longdie的大佬的单调队列优化的n^2dp给碾压了,而我这一块并不是十分的熟练,于是找了一道接近板子的题目......
考虑维护r-l+1之中可以转移到一个位置的长度里面的最大值,既然要求最大值,重点又确定(指定某一位置),这样做当然不会更差,在维护到结尾之后对可以跳出去的位置统计一下就可以了.
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e6+10; 4 int n,l,r,ans=-2147483647,val[N],dp[N]; 5 deque<int>q; 6 int main(){ 7 scanf("%d%d%d",&n,&l,&r); 8 for(int i=0;i<=n;++i) scanf("%d",&val[i]); 9 memset(dp,128,sizeof(dp));dp[0]=0; 10 for(int i=l;i<=n;++i){ 11 while(!q.empty() && dp[i-l]>=dp[q.back()]) q.pop_back(); 12 while(!q.empty() && i-q.front()>r) q.pop_front(); 13 q.push_back(i-l); 14 dp[i]=dp[q.front()]+val[i]; 15 } 16 for(int i=n+1-r;i<=n;++i) ans=max(ans,dp[i]); 17 printf("%d ",ans); 18 }
一道区间dp的基础题目
这道题最初想复杂了,lc大佬也A的很懵,于是我们就从8点多到九点一直在调我的垃圾代码......
如果a[i]==a[j],我们有dp[i][j]=min(dp[i][j],min(dp[i+1][j],dp[i][j-1]));这道题的精华就在这个式子之上了,如果a[i]==a[j],那么我们可以从相邻的两个状态进行转移,因为既然两个边界相等,那么我们可以在原来基础上把i到j都先刷上同一种颜色,这样就不用在费一次的刷子次数了,之后再套上一个区间dp的板子格式,把上面的式子嵌入其中就可以了.
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e6+10; 4 char a[N]; 5 int dp[100][100]; 6 int vis[51][51][100]; 7 int main(){ 8 scanf(" %s",a+1); 9 int len=strlen(a+1); 10 for(int i=1;i<=len;++i) dp[i][i]=1; 11 for(int i=1;i<=len;++i) 12 for(int j=i+1;j<=len;++j) 13 dp[i][j]=0x3f3f3f3f; 14 for(int d=2;d<=len;++d) 15 for(int i=1,j;(j=i+d-1)<=len;++i){ 16 if(a[i]==a[j]) dp[i][j]=min(dp[i][j],min(dp[i+1][j],dp[i][j-1])); 17 for(int k=i;k<j;++k){ 18 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); 19 } 20 //printf("%d %d %d ",i,j,dp[i][j]); 21 } 22 printf("%d ",dp[1][len]);//dedcecbcd 5 23 //dbdcd 3 24 return 0; 25 }
总结:
今天(或者说是昨天)可能随着集训的进行状态有些下滑,于是我的成绩对我进行了警告处分......
晚上吃完饭后又被文化课的班主任拉了去打扫高考教室,班主任一边在旁边开班会,好像是期末动员???(反正我不用参加qwq)
现在我对于学校考试的感觉是虎哥出题没思路,老姚出题调不出,老姚虎哥相结合,明天爆零不是梦
过几天就要高考了,祝愿高三学长们和我都能取得一个好成绩吧.