P9752 [CSP-S 2023] 密码锁
直接模拟统计就可以了,看每个状态有多少个转移的状态,就是输入的所有状态里面,把所以可能是由此转化来的++,最后循环所有的情况,如果能够转移的数量为n,那么就是结果++
#include <bits/stdc++.h> typedef long long LL; using namespace std; const int maxn=1e5+10; int n,ans,dp[11][11][11][11][11]; int main() { //咋个就一开始没转过来呢 cin>>n; for(int i=1;i<=n;i++){ int a,b,c,d,e; cin>>a>>b>>c>>d>>e; for(int j=1;j<=9;j++){ dp[(a+j)%10][b][c][d][e]++; dp[a][(b+j)%10][c][d][e]++; dp[a][b][(c+j)%10][d][e]++; dp[a][b][c][(d+j)%10][e]++; dp[a][b][c][d][(e+j)%10]++; dp[(a+j)%10][(b+j)%10][c][d][e]++; dp[a][(b+j)%10][(c+j)%10][d][e]++; dp[a][b][(c+j)%10][(d+j)%10][e]++; dp[a][b][c][(d+j)%10][(e+j)%10]++; } } for(int i=0;i<=9;i++){ for(int j=0;j<=9;j++){ for(int k=0;k<=9;k++){ for(int t=0;t<=9;t++){ for(int p=0;p<=9;p++){ if(dp[i][j][k][t][p]==n) ans++; } } } } } cout<<ans<<endl; return 0; }
P9753 [CSP-S 2023] 消消乐
类似于kmp思想,记录跳转的地方,让fi表示以i结尾的合法区间个数,fi=fj+1,那么(j+1,i)是一个合法的区间
要保证最大。那么(j+1,i)是以i结尾的最短合法区间,这里就是可以直接跳转
#include <bits/stdc++.h> typedef long long LL; using namespace std; const int maxn=2e6+10; //感觉有点kmp算法的思想 //记录跳转的地方,让fi表示以i结尾的合法区间个数,fi=fj+1,那么(j+1,i)是一个合法的区间 //要保证最大。那么(j+1,i)是以i结尾的最短合法区间 int dp[maxn] ,las[maxn]; int main() { int n;cin>>n; string s; cin>>s; s='%'+s; //加个东西 LL ans=0; for(int i=1;i<=n;i++){ for(int j=i-1;j>0;j=las[j]-1){ if(s[i]==s[j]){ las[i]=j;break; } } if(las[i]) dp[i]=dp[las[i]-1]+1,ans+=dp[i]; } cout<<ans<<endl; return 0; }
P9754 [CSP-S 2023] 结构体
大模拟,很多细节,结构体、指针的运用,仔细就可以,没特别复杂
#include <bits/stdc++.h> using namespace std; #define LL long long const int maxn=2e6+10; //这个模拟比较难写,很多细节、结构体、指针很多应用 struct jiegou{ string name,type; //别名和类型 LL len,dp,py; //长度、对齐、相对父亲的偏移 vector<jiegou *>son; //儿子(内涵的结构体) jiegou(){ py=len=dp=0;name=type=""; } jiegou(LL len,string tp){ this->py=0;this->len=len;this->dp=len; this->type=tp; } }; map<string,jiegou> mp; //初始化结构体 LL js(LL a,LL b){ return a/b+int(a%b>0); //向上取整,计算偏移的时候需要 } void build(string x,LL k){ //类型是x,里面有k个元素 jiegou tmp; tmp.type=x; LL py=0; jiegou *las; for(LL i=1;i<=k;i++){ string tpe,bieming; cin>>tpe>>bieming; jiegou *tmp1=new jiegou(mp[tpe]); tmp1->name=bieming; tmp.dp=max(tmp.dp,tmp1->dp); //对齐长度取长度最大的元素 if(i>1){ tmp1->py=tmp1->dp*js(las->py+las->len,tmp1->dp); //计算这个元素的起始位置 //就是这个元素的对齐长度乘以 上一个元素的偏移量加上长度除以这个元素的对齐长度并向上取整 } tmp.son.push_back(tmp1); las=tmp1; } tmp.len=tmp.dp*js(las->len+las->py,tmp.dp); //整个结构体的长度 mp[x]=tmp; cout<<tmp.len<<" "<<tmp.dp<<endl; //输出结构体的长度和对齐 } // 2 -> 加入内存 jiegou *tr=new jiegou(); //初识时没有初始化的结构体变量 void add(string tpe,string name){ jiegou *tmp=new jiegou(mp[tpe]); tmp->name=name; LL x,y,st=0; vector<jiegou *> vec=tr->son; //一开始是空的!!现在创建一个加入一个 if(vec.empty()){ x=0;y=0; } else{ x=(*--vec.end())->py; y=(*--vec.end())->len; } st=js(x+y,tmp->dp)*tmp->dp; //偏移值 tmp->py=st; //这个元素的偏移 tr->son.push_back(tmp); cout<<st<<endl; } //3 -->直接暴力分割出来每一层索引的名字然后暴力一层一层跳就可以了 LL findd(string x){ vector<string> vec; LL las=0,len=x.length(); for(LL i=1;i<=len;i++){ if(x[i-1]=='.') vec.push_back(x.substr(las,i-las-1)),las=i; //一层层放进去 } vec.push_back(x.substr(las,len-las)); jiegou *now=tr; LL st=0,py; for(LL i=1;i<=vec.size();i++){ py=st; for(int j=1;j<=now->son.size();j++){ jiegou *k=now->son[j-1]; py=st+k->py; if(k->name==vec[i-1]){ st=py; now=k; //这个也在往里走,往里面搜索每一个元素 break; } } } return py; } // 4 -->递归找一下,暴力一层层跳,递归存一下目前的总偏移量,如果子节点包含这个区间就往下找 string getb(jiegou *now,LL st,LL tar){ string ans=""; for(LL i=1;i<=now->son.size();i++){ jiegou *k=now->son[i-1]; if(st+(k->py)<=tar&&tar<st+(k->py)+(k->len)){ //在这个结构体里面 ans=k->name; if(k->son.size()) ans+="."+getb(k,st+(k->py),tar); break; } } if(ans.empty()) return "ERR"; //如果要返回err,实际上只有最后一层是err,因为有可能在一个结构体里面,知只是没有被占据 else return ans; } int main() { LL n; cin>>n; mp["long"]=jiegou(8ll,"long"); mp["int"]=jiegou(4ll,"int"); mp["short"]=jiegou(2ll,"short"); mp["byte"]=jiegou(1ll,"byte"); for(LL i=1;i<=n;i++){ int opt; cin>>opt; if(opt==1){ string x;LL y; cin>>x>>y; build(x,y); } else if(opt==2){ string x,y; cin>>x>>y; add(x,y); } else if(opt==3){ string x;cin>>x; cout<<findd(x)<<endl; } else if(opt==4){ LL adr;cin>>adr; string ans=getb(tr,0,adr); if(ans.find("ERR")!=-1) cout<<"ERR"<<endl; else cout<<ans<<endl; } } return 0; }
P9755 [CSP-S 2023] 种树
虽然知道有单调性 但是要写出二分还是比较难 --- 二层二分
https://www.luogu.com.cn/blog/979266/p9755-csp-s-2023-zhong-shu
写的比较好,首先因为bi+cix 这个是单调的所以可以从这里入手二分(区分什么时候取bi+cix什么时候取1),我们可以对于每一个地块i,求出最晚种树且到 x天时能到达 ai的天数ki
对于每个点i可以在1——ki之间任意一个时间种树,每个时间只能种一棵树,球有没有满足的方案
贪心做法:将ki从小到大排序,然后按照树的结构去判断是否有这样的方案(暴力枚举每个点到父亲的路上是否中了树)并且这个时间是否超过ki
开__int128
#include <bits/stdc++.h> using namespace std; typedef __int128 LL; const int maxn=1e5+100; const int INF=1e10; //。虽然知道有单调性 但是要写出二分还是比较难 --- 二层二分 //https://www.luogu.com.cn/blog/979266/p9755-csp-s-2023-zhong-shu 写的比较好 //首先因为bi+cix 这个是单调的所以可以从这里入手二分(区分什么时候取bi+cix什么时候取1),我们可以对于每一个地块i,求出最晚种树且到 x天时能到达 ai的天数ki //对于每个点i可以在1——ki之间任意一个时间种树,每个时间只能种一棵树,球有没有满足的方案 //贪心做法:将ki从小到大排序,然后按照树的结构去判断是否有这样的方案(暴力枚举每个点到父亲的路上是否中了树)并且这个时间是否超过ki //开__int128 inline LL read(){ LL x=0,f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } inline void write(LL x){ if(x<0){ putchar('-'); x=-x; } if(x>9) write(x/10); putchar(x%10+'0'); } struct node{ LL x,y; //表示最晚开始的天数,节点标号 bool operator < (const node &yyy)const{ return x<yyy.x; } }k[maxn]; LL n; LL a[maxn],b[maxn],c[maxn],h[maxn],p[maxn],fa[maxn]; //h数组就是每个节点的最晚种树节点 bool vis[maxn]; vector<LL> e[maxn]; //树 void adde(LL u,LL v){ e[u].push_back(v); e[v].push_back(u); } void dfs(LL u,LL f){ for(int i=0;i<e[u].size();i++){ LL op=e[u][i]; if(op==f) continue; fa[op]=u; dfs(op,u); } } LL js(LL i,LL l,LL r){ //计算第i课树,从l到r天的生长总长度 LL t=r-l+1; if(h[i]<l) return t; if(h[i]>r) return t*b[i]+(((l+r)*t)/2)*c[i]; //只有前半部分 return (r-h[i])+(h[i]-l+1)*b[i]+(((l+h[i])*(h[i]-l+1))/2)*c[i]; } bool check(LL x){ for(int i=1;i<=n;i++){ if(js(i,1,x)<a[i]) return 0; //如果最多x天,那么就算是从第一天开始也不行,说明肯定不是合法的方案 LL l=1,r=n; //看从第几个开始种树 while(l<r){ LL mid=(l+r+1)>>1; if(js(i,mid,x)>=a[i]) l=mid; else r=mid-1; } k[i]={l,i}; p[i]=k[i].x; //最晚开始的时间 要多余记录这个,因为后面要比较 } memset(vis,0,sizeof(vis)); sort(k+1,k+1+n); vis[0]=1; LL ttt=0; for(int i=1;i<=n;i++){ stack<LL> d; LL t=k[i].y; while(!vis[t]){ d.push(t); vis[d.top()]=1; t=fa[t]; //往上走 因为肯定这些没有被种 } while(!d.empty()){ ++ttt; if(p[d.top()]<ttt) return 0; //如果这个时间开始满足在最晚开始时间之前 d.pop(); } } return 1; } int main(){ // scanf("%lld",&n); // for(int i=1;i<=n;i++) scanf("%lld %lld %lld",&a[i],&b[i],&c[i]); // for(LL u,v,i=1;i<=n-1;i++){ // scanf("%lld %lld",&u,&v);adde(u,v); // } n=read(); for(int i=1;i<=n;i++){ a[i]=read(); b[i]=read(); c[i]=read(); } for(int u,v,i=1;i<n;i++){ u=read(),v=read(); adde(u,v); } for(int i=1;i<=n;i++){ if(c[i]>=0) h[i]=INF; else h[i]=(1-b[i])/c[i]; } dfs(1,1); LL l=n,r=1e9; //l起始值为n while(l<r){ LL mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid+1; } write(l); return 0; }