
[{"content":"记录一些修改过后的东西。包含 修改后的 alert、\n火 信息 这是 √ 按钮 Windows macOS Linux 使用 Chocolatey 安装:\nchoco install vscode.install 或使用 WinGet 安装\nwinget install -e --id Microsoft.VisualStudioCode brew install --cask visual-studio-code 参见文档。 右边的字 ","date":"17 April 2026","externalUrl":null,"permalink":"/20260417204400.html/","section":"Posts","summary":"","title":"备忘录","type":"posts"},{"content":"","date":"17 April 2026","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"17 April 2026","externalUrl":null,"permalink":"/","section":"xyx404","summary":"","title":"xyx404","type":"page"},{"content":"","date":"2 October 2025","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2 October 2025","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2 October 2025","externalUrl":null,"permalink":"/tags/%E4%BA%A4%E4%BA%92%E9%A2%98/","section":"Tags","summary":"","title":"交互题","type":"tags"},{"content":"","date":"2 October 2025","externalUrl":null,"permalink":"/categories/%E6%B4%9B%E8%B0%B7%E9%A2%98%E8%A7%A3/","section":"Categories","summary":"","title":"洛谷题解","type":"categories"},{"content":"{% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/gfxhuzz7 %}\n思路： # 通过将炮塔放置在行和列均为 $K$ 的倍数的位置上，可以确保每个 $K\\times K$ 的子矩阵都包含至少一个这样的炮塔。\n也就是说行方向需要 $\\lfloor N/K \\rfloor$ 个炮塔，列方向需要 $\\lfloor M/K \\rfloor$ 个炮塔，共需要 $\\lfloor N/K \\rfloor \\times \\lfloor M/K \\rfloor$ 个炮塔。\n这样便是最优的。\n代码： # {% tabs code %}\n#define int32 int32_t int32 solve(int32 N, int32 M, int32 K){ int32 ans=0; for(int i=K;i\u0026lt;=N;i+=K){ for(int j=K;j\u0026lt;=M;j+=K){ ans++; } } return ans; } #define int32 int32_t int32 solve(int32 N, int32 M, int32 K){ return (N/K)*(M/K); } {% endtabs %}\n","date":"2 October 2025","externalUrl":null,"permalink":"/20251002190600.html/","section":"Posts","summary":"","title":"题解：P14041 [PAIO 2025] Towers","type":"posts"},{"content":"{% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/k61kz6f9 %}\n{% folding blue open, 简化题意 %}\n有一个有 $n$ 层的架子，初始时在第一层（最高层设为第 $1$ 层，最低层设为第 $n$ 层）。\n第 $i$ 层上有 $q_i$ 个食物，对于第 $i$ 层第 $j$ 份食物，它可以提供 $c_{i,j}$ 的卡路里，并且有 $x_{i,j}$ 的概率选到它。\n最多一次向下 $k$ 层，往下 $i$ 层的概率为 $p_i$。\n每当到达某一层时，选择一份食物吃掉，得到它提供的卡路里，然后前往下一层。\n如果当前层数大于 $n$ 则称为离开了架子。\n输出在离开架子前，预期会吸收多少卡路里？\n{% endfolding %}\n{% folding blue, 思路 %}\n我们定义 $dp$ 数组，$dp_i$ 表示到达第 $i$ 层的概率。\n因为我们初始在第 $1$ 层，所以初始化时 $dp_1$ 等于 $1$。\n期望总卡路里等于每一层 $i$ 的 $dp_i$ 乘以该层的期望卡路里之和。\n{% endfolding %}\n{% folding green, 代码 %}\n#include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int T; int n,k; int main(){ // ios::sync_with_stdio(0); // cin.tie(0);cout.tie(0); cin\u0026gt;\u0026gt;T; for(int i=1;i\u0026lt;=T;i++){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;k; vector\u0026lt;double\u0026gt;p(k+1),dp(n+1,0); dp[1]=1; for(int i=1;i\u0026lt;=k;i++)cin\u0026gt;\u0026gt;p[i]; double ans=0; for(int i=1;i\u0026lt;=n;i++){ int l;cin\u0026gt;\u0026gt;l; double sum=0; for(int j=1;j\u0026lt;=l;j++){ double c,x;cin\u0026gt;\u0026gt;c\u0026gt;\u0026gt;x; sum+=c*x; } for(int j=1;j\u0026lt;=k\u0026amp;\u0026amp;i-j\u0026gt;0;j++){ dp[i]+=dp[i-j]*p[j]; } ans+=dp[i]*sum; } printf(\u0026#34;Case #%d: %.6lf\\n\u0026#34;,i,ans); } return 0; } /* ｀　４　０００　４ ４４　０　０　４４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４４　０　０　４４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４　４　０　０　４　４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４　４　０　０　４　４ ｘ　ｙ　ｙ　ｘ　４　４　０　０　４　４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４４４４４　０　０　４４４４４ ｘ　ｘ　ｙ　ｘ　ｘ　４　０　０　４ ｘ　ｘ　ｙ　ｘ　ｘ　４　０００　４ ｙｙ */ {% endfolding %}\n","date":"1 October 2025","externalUrl":null,"permalink":"/20251001090800.html/","section":"Posts","summary":"","title":"题解：UVA12723 Dudu, the Possum","type":"posts"},{"content":"","date":"13 September 2025","externalUrl":null,"permalink":"/tags/%E5%B7%AE%E5%88%86/","section":"Tags","summary":"","title":"差分","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/ec4lo6xq %}\n思路： # 首先，考虑极差为 $0$ 的情况需要满足什么：\n假设第 $i$ 个数可以被修改 $sum_i$ 次，那么这个数的最大值为 $a_i + sum_i$，最小值为 $a_i - sum_i$，也就是说这个数可以是区间 $[a_i-sum_i,a_i+sum_i]$ 中的任意数。只要有一个数在所有 $a_i$ 被修改后可以是的区间内，那么就说明修改后的 $a_i$ 可以全部相等，也就是极差为 $0$。\n考虑用差分维护可以被修改的次数。\n对于其它情况：\n我们知道极差是序列里最大值和最小值的差，于是我们可以把所有可能的最大值里的最小值和所有可能的最小值里的最大值做差即可。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL maxx[int(2*1e5+10)],minn[int(2*1e5+10)]; int diff[int(2*1e5+10)]; LL sum; int n,m,T,u,v; LL a[int(2*1e5+10)]; void solve(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; diff[0]=diff[n+1]=0;sum=0; for(int i=1;i\u0026lt;=n;i++){ cin\u0026gt;\u0026gt;a[i];diff[i]=0; } for(int i=1;i\u0026lt;=m;i++){ cin\u0026gt;\u0026gt;u\u0026gt;\u0026gt;v; diff[u]++;diff[v+1]--; } bool bj=0; for(int i=1;i\u0026lt;=n;i++){ sum+=diff[i]; maxx[i]=a[i]+sum; minn[i]=a[i]-sum; // cout\u0026lt;\u0026lt;sum\u0026lt;\u0026lt;\u0026#34; \u0026#34;; } LL ma=maxx[1],mi=minn[1]; LL l=1e9+10,r=0; for(int i=2;i\u0026lt;=n;i++){ if(maxx[i]\u0026lt;mi||minn[i]\u0026gt;ma){ bj=1; } mi=max(mi,minn[i]); ma=min(maxx[i],ma); // l=min(l,minn[i]); // r=max(r,maxx[i]); } if(!bj){ cout\u0026lt;\u0026lt;\u0026#34;0\\n\u0026#34;; return; } cout\u0026lt;\u0026lt;abs(mi-ma)\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; return; } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin\u0026gt;\u0026gt;T; for(;T--;solve()); return 0; } /* ｀　４　０００　４ ４４　０　０　４４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４４　０　０　４４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４　４　０　０　４　４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４　４　０　０　４　４ ｘ　ｙ　ｙ　ｘ　４　４　０　０　４　４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４４４４４　０　０　４４４４４ ｘ　ｘ　ｙ　ｘ　ｘ　４　０　０　４ ｘ　ｘ　ｙ　ｘ　ｘ　４　０００　４ ｙｙ */ ","date":"13 September 2025","externalUrl":null,"permalink":"/20250913195700.html/","section":"Posts","summary":"","title":"题解：P14028 【MX-X20-T2】「FAOI-R7」最小极差（jicha）","type":"posts"},{"content":"","date":"24 August 2025","externalUrl":null,"permalink":"/categories/uva/","section":"Categories","summary":"","title":"UVA","type":"categories"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/m2xb6vwa %}\n简要题意 # 给出多组字符串，你需要输出每个字符串中出现次数最多的字母（小写输出），如果有多个，则按字典序输出。\n输入可能包含大写字母，但是也要计入字母输出次数。\n字符串中间可能有空格。\n思路 # 因为字符串中间可能有空格，所以我们需要整行输入，使用 getlline。\n考虑使用 map 作为桶。\n把字符串的字母全部放进桶中。\n处理完字符串后找到出现次数最多的字母，最后按字典序输出。\n代码 # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int T; string s; int main(){ //ios::sync_with_stdio(0); //cin.tie(0);cout.tie(0); 这个注释删了就不能用 getchar() 了 cin\u0026gt;\u0026gt;T; getchar(); while(T--){ getline(cin,s); //cout\u0026lt;\u0026lt;s\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; unordered_map\u0026lt;char,int\u0026gt;mp; for(int i=0;i\u0026lt;s.size();i++){ if(s[i]\u0026lt;=\u0026#39;z\u0026#39;\u0026amp;\u0026amp;s[i]\u0026gt;=\u0026#39;a\u0026#39;)mp[s[i]]++; if(s[i]\u0026lt;=\u0026#39;Z\u0026#39;\u0026amp;\u0026amp;s[i]\u0026gt;=\u0026#39;A\u0026#39;)mp[s[i]-\u0026#39;A\u0026#39;+\u0026#39;a\u0026#39;]++; } int maxx=0; for(char i=\u0026#39;a\u0026#39;;i\u0026lt;=\u0026#39;z\u0026#39;;i++){ maxx=max(maxx,mp[i]); } for(char i=\u0026#39;a\u0026#39;;i\u0026lt;=\u0026#39;z\u0026#39;;i++){ if(mp[i]==maxx)cout\u0026lt;\u0026lt;i; } cout\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } /* ｀　４　０００　４ ４４　０　０　４４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４４　０　０　４４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４　４　０　０　４　４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４　４　０　０　４　４ ｘ　ｙ　ｙ　ｘ　４　４　０　０　４　４ ｘ　ｘ　ｙ　ｙ　ｘ　ｘ　４４４４４　０　０　４４４４４ ｘ　ｘ　ｙ　ｘ　ｘ　４　０　０　４ ｘ　ｘ　ｙ　ｘ　ｘ　４　０００　４ ｙｙ */ ","date":"24 August 2025","externalUrl":null,"permalink":"/20250824162800.html/","section":"Posts","summary":"","title":"题解：UVA11577 Letter Frequency","type":"posts"},{"content":"","date":"24 August 2025","externalUrl":null,"permalink":"/tags/%E5%AD%97%E7%AC%A6%E4%B8%B2/","section":"Tags","summary":"","title":"字符串","type":"tags"},{"content":"","date":"26 July 2025","externalUrl":null,"permalink":"/tags/%E8%B4%AA%E5%BF%83/","section":"Tags","summary":"","title":"贪心","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/9jwfekli %}\n简要题意： # 给定两个 vector，$v_1 = (x_1, x_2, \\cdots, x_n)$ 和 $v_2 = (y_1, y_2,\\cdots, y_n)$，要求最小化标量积 $x_1 \\times y_1 + x_2 \\times y_2+ \\cdots + x_n \\times y_n$，两个 vector 中的元素可以任意更改位置。\n思路： # 将 $v_1$ 和 $v_2$ 分别按升序和降序进行排序，然后计算即可。\n证明在最后。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int T; LL a[810],b[810]; int cnt; int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin\u0026gt;\u0026gt;T; while(T--){ cnt++; int n;cin\u0026gt;\u0026gt;n; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;a[i]; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;b[i]; sort(a+1,a+1+n); sort(b+1,b+1+n); LL ans=0; for(int i=1,j=n;i\u0026lt;=n;i++,j--){ ans+=a[i]*b[j]; } cout\u0026lt;\u0026lt;\u0026#34;Case #\u0026#34;\u0026lt;\u0026lt;cnt\u0026lt;\u0026lt;\u0026#34;: \u0026#34;\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } 证明： # 尽管比较显然，但我们还是证明一下，考虑反证法。\n假设存在另一种配对方式，使得标量积比 $v_1$ 升序，$v_2$ 降序的配对方式更小。\n设 $v_1$ 按升序排序 $x_1 \\le x_2 \\le \\cdots \\le x_n$，$v_2$ 降序排序 $y_1 \\ge y_2 \\ge \\cdots y_n$，这样的标量积为 $S_1 = x_1 \\times y_1 + x_2 \\times y_2 + \\cdots + x_n \\times y_n$。\n假设存在另一种配对方式，存在两个位置 $i$ 和 $j$，$x_i$ 和 $y_j$ 配对，$x_j$ 和 $y_i$ 配对，且这种配对的标量积 $S_2 \u003c S_1$，则需要满足 $x_i \\times y_i + x_j \\times y_j \u003e x_i \\times y_j + x_j \\times y_i$。\n考虑两种配对方式带来的差异：\n$x_i$ 和 $y_i$ 配对，$x_j$ 和 $y_j$ 配对，和为 $x_i \\times y_i + x_j \\times y_j$。 $x_i$ 和 $y_j$ 配对，$x_j$ 和 $y_i$ 配对，和为 $x_i \\times y_j + x_j \\times y_i$。 我们计算差值，以此比较大小：\n因此，不存在比 $v_1$ 升序，$v_2$ 降序的配对方式更小的标量积。\n故代码正确。\n","date":"26 July 2025","externalUrl":null,"permalink":"/20250726085700.html/","section":"Posts","summary":"","title":"题解：P13457Minimum Scalar Product","type":"posts"},{"content":"","date":"24 July 2025","externalUrl":null,"permalink":"/tags/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97/","section":"Tags","summary":"","title":"斐波那契数列","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/ak4f18tr %}\n思路： # 对于用斐波那契数列将距离从英里转换为公里，我们可以发现一个性质，误差最小的公里数必然是距离真实值 $1.6 \\times n$ 最近的两个整数之一。\n通过拆分英里数，我们可以用斐波那契数列中小的数组合出任何大于等于 $2$ 的整数公里数。所以我们也可以组合出与真实值最近的两个整数，我们只需要在两个整数与真实值的差中取最小值就好了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n; int main(){ //ios::sync_with_stdio(0); //cin.tie(0);cout.tie(0); while(cin\u0026gt;\u0026gt;n){ if(!n)break; int a=1.6*n,b=a+1; printf(\u0026#34;%.2lf\\n\u0026#34;,min(abs(1.6*n-a),abs(b-1.6*n))); } return 0; } ","date":"24 July 2025","externalUrl":null,"permalink":"/20250724212000.html/","section":"Posts","summary":"","title":"题解：UVA11780 Miles 2 Km","type":"posts"},{"content":"","date":"23 July 2025","externalUrl":null,"permalink":"/categories/%E8%93%9D%E6%A1%A5%E6%9D%AF%E5%9B%BD%E8%B5%9B/","section":"Categories","summary":"","title":"蓝桥杯国赛","type":"categories"},{"content":"","date":"23 July 2025","externalUrl":null,"permalink":"/tags/%E6%95%B0%E4%BD%8D-dp/","section":"Tags","summary":"","title":"数位 DP","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/ced1wtab %}\n前言： # 没学过数位 DP 的可以参考 CSP Wiki。\n思路： # 考虑数位 DP。\n本题只需要在填数时判断一下当前相邻数位差的绝对值之和有没有大于 $m$ 就行了。\n具体地，对于每次填数，我们需要考虑，当前相邻数位差的绝对值之和有没有大于 $m$。\n如果大于，返回本次 $0$ 表示没有答案；否则，我们考虑当前数位是否有限制，接着填数，如果最后一位也填了，并且相邻数位差的绝对值之和没有大于 $m$ 就代表本次填的答案是可行的。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL n,m; LL dp[20][300][11]; int a[20]; int cf(LL x){ int len=0; while(x){ a[++len]=x%10; x/=10; } return len; } LL dfs(LL now,bool xz,bool tg,int sum,int las){ LL ans=0; if(sum\u0026gt;m)return 0; if(now==0)return 1; if(!xz\u0026amp;\u0026amp;!tg\u0026amp;\u0026amp;dp[now][sum][las]!=-1)return dp[now][sum][las]; int l=0,r=9; if(tg)l=1,ans+=dfs(now-1,0,tg,0,-1); if(xz)r=a[now]; for(int i=l;i\u0026lt;=r;i++){ ans+=dfs(now-1,(i==r\u0026amp;\u0026amp;xz),0,(las!=-1?sum+abs(i-las):0),i); } if(!xz\u0026amp;\u0026amp;!tg)dp[now][sum][las]=ans; return ans; } LL ans(LL x){ int len=cf(x); memset(dp,-1,sizeof dp); return dfs(len,1,1,0,-1); } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; cout\u0026lt;\u0026lt;ans(n); return 0; } ","date":"23 July 2025","externalUrl":null,"permalink":"/20250723113200.html/","section":"Posts","summary":"","title":"题解：P12884 [蓝桥杯 2025 国 C] 整齐的数","type":"posts"},{"content":"","date":"17 July 2025","externalUrl":null,"permalink":"/tags/%E6%8E%92%E5%BA%8F/","section":"Tags","summary":"","title":"排序","type":"tags"},{"content":"","date":"17 July 2025","externalUrl":null,"permalink":"/tags/%E6%89%AB%E6%8F%8F%E7%BA%BF/","section":"Tags","summary":"","title":"扫描线","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/fy7o4a2c %}\n简要题意： # 多组数据。\n每组数据给定 $n$（$0 \\le n \\le 1000$）个区间 $[s_i,e_i]$（$0 \\le s_i \\le e_i \\le 31536000$），求所有时刻中同时存在的区间数量的最大值。\n思路： # 考虑扫描线算法。\n对于扫描线的基本步骤，可以参考此博文。\n将每个区间的开始点和结束点存下来，然后按照时间顺序排序，如果是起点让计数器加一，否则是终点让计数器减一，我们只需要在计算器里取最大值就好了。\n因为终点也算在区间里，所以我们在处理时要优先处理起点。\n对于每组数据，时间复杂度为 $O(n \\log n)$。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n; int Case_Cnt=0; struct node{ int x,lx; friend bool operator\u0026lt;(node x,node y){ return x.x!=y.x?x.x\u0026lt;y.x:x.lx\u0026gt;y.lx; } }; int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); while(cin\u0026gt;\u0026gt;n){ if(n==-1)break; int ans=0; cout\u0026lt;\u0026lt;\u0026#34;Case \u0026#34;\u0026lt;\u0026lt;++Case_Cnt\u0026lt;\u0026lt;\u0026#34;: \u0026#34;; vector\u0026lt;node\u0026gt;sz; for(int i=1;i\u0026lt;=n;i++){ int s,e; cin\u0026gt;\u0026gt;s\u0026gt;\u0026gt;e; sz.emplace_back(node{s,1}); sz.emplace_back(node{e,-1}); } sort(sz.begin(),sz.end()); int now=0; for(int i=0;i\u0026lt;n*2;i++){ ans=max(ans,now); now+=sz[i].lx; } ans=max(ans,now); cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } ","date":"17 July 2025","externalUrl":null,"permalink":"/20250717104800.html/","section":"Posts","summary":"","title":"题解：UVA11776 Oh Your Royal Greediness!","type":"posts"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/nny6d3kh %}\n思路： # 通过观察可以发现有一些字母在所有数字中只出现过一次，我们可以先处理这些字母对应的单词。\n比如字母 Z 只出现在 ZERO 中。\n在处理完这些后，我们可以发现有一些字母在这些单词中只出现过两次，而其中一个单词已经处理过了，这时我们就可以直接处理另外一个单词。\n例如 SIX 和 SEVEN，SIX 中的 X 只在它自己里面出现，已经被处理了，而 S 只在这两个单词中出现，所以剩下的 S 都是 $7$ 的了。\n以此类推，我们便可以完成此题。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int T; string s; int main(){ cin\u0026gt;\u0026gt;T; for(int i=1;i\u0026lt;=T;i++){ cin\u0026gt;\u0026gt;s; unordered_map\u0026lt;char,int\u0026gt;cnt; int bj[12];memset(bj,0,sizeof bj); for(int i=0;i\u0026lt;s.size();i++)cnt[s[i]]++; while(cnt[\u0026#39;Z\u0026#39;]){// ZERO 0 Z 只在 0 中出现 cnt[\u0026#39;Z\u0026#39;]--;cnt[\u0026#39;E\u0026#39;]--;cnt[\u0026#39;R\u0026#39;]--; cnt[\u0026#39;O\u0026#39;]--; bj[0]++; } while(cnt[\u0026#39;W\u0026#39;]){// TWO 2 cnt[\u0026#39;T\u0026#39;]--;cnt[\u0026#39;W\u0026#39;]--;cnt[\u0026#39;O\u0026#39;]--; bj[2]++; } while(cnt[\u0026#39;G\u0026#39;]){// EIGHT 8 cnt[\u0026#39;E\u0026#39;]--;cnt[\u0026#39;I\u0026#39;]--;cnt[\u0026#39;G\u0026#39;]--; cnt[\u0026#39;H\u0026#39;]--;cnt[\u0026#39;T\u0026#39;]--; bj[8]++; } while(cnt[\u0026#39;X\u0026#39;]){// SIX 6 cnt[\u0026#39;S\u0026#39;]--;cnt[\u0026#39;I\u0026#39;]--;cnt[\u0026#39;X\u0026#39;]--; bj[6]++; } while(cnt[\u0026#39;U\u0026#39;]){// FOUR 4 cnt[\u0026#39;F\u0026#39;]--;cnt[\u0026#39;O\u0026#39;]--;cnt[\u0026#39;U\u0026#39;]--; cnt[\u0026#39;R\u0026#39;]--; bj[4]++; } while(cnt[\u0026#39;S\u0026#39;]){// SEVEN 7 因为有 S 出现的只有 6 和 7，6 已经处理完了，剩下的 S 都是 7 的了 cnt[\u0026#39;S\u0026#39;]--;cnt[\u0026#39;E\u0026#39;]--;cnt[\u0026#39;V\u0026#39;]--; cnt[\u0026#39;E\u0026#39;]--;cnt[\u0026#39;N\u0026#39;]--; bj[7]++; } while(cnt[\u0026#39;F\u0026#39;]){// FIVE 5 cnt[\u0026#39;F\u0026#39;]--;cnt[\u0026#39;I\u0026#39;]--;cnt[\u0026#39;V\u0026#39;]--; cnt[\u0026#39;E\u0026#39;]--; bj[5]++; } while(cnt[\u0026#39;H\u0026#39;]){// THREE 3 cnt[\u0026#39;T\u0026#39;]--;cnt[\u0026#39;H\u0026#39;]--;cnt[\u0026#39;R\u0026#39;]--; cnt[\u0026#39;E\u0026#39;]--;cnt[\u0026#39;E\u0026#39;]--; bj[3]++; } while(cnt[\u0026#39;I\u0026#39;]){// NINE 9 cnt[\u0026#39;N\u0026#39;]--;cnt[\u0026#39;I\u0026#39;]--;cnt[\u0026#39;N\u0026#39;]--; cnt[\u0026#39;E\u0026#39;]--; bj[9]++; } while(cnt[\u0026#39;O\u0026#39;]){// ONE 1 cnt[\u0026#39;O\u0026#39;]--;cnt[\u0026#39;N\u0026#39;]--;cnt[\u0026#39;E\u0026#39;]--; bj[1]++; } cout\u0026lt;\u0026lt;\u0026#34;Case #\u0026#34;\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#34;: \u0026#34;; for(int i=0;i\u0026lt;=9;i++){ while(bj[i])cout\u0026lt;\u0026lt;i,bj[i]--; } cout\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } ","date":"10 July 2025","externalUrl":null,"permalink":"/20250710215400.html/","section":"Posts","summary":"","title":"题解：P13192Getting the Digits","type":"posts"},{"content":"","date":"4 July 2025","externalUrl":null,"permalink":"/tags/dfs/","section":"Tags","summary":"","title":"Dfs","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/sofzxmhd %}\n题意简述： # 多组数据。\n每组数据给出两个字符串（源字符串和目标字符串），输出所有通过栈操作将源单词转为目标单词的操作字符串（i 表示入栈，o 表示出栈）。\n操作字符串按字典序输出。\n思路： # 使用 DFS 算法，暴力模拟出栈入栈操作，为了保证按字典序从小到大输出，应该优先模拟入栈操作。因为入栈用字母 i 表示，出栈用字母 o 表示，很明显 i 的字典序比 o 小。\n特别注意：本题不允许行末多余空格。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long string st,en; stack\u0026lt;char\u0026gt;sta; int lenen; void dfs(int s,int e,string op/*操作*/){ if(e==lenen){ cout\u0026lt;\u0026lt;op\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; return; } //\tcout\u0026lt;\u0026lt;op\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; // i 的 ASCII 码值小于 o 为了保证字典序从小到大，先从小的开始 if(s\u0026lt;st.size()){// 模拟入栈 sta.push(st[s]); dfs(s+1,e,op+\u0026#34;i \u0026#34;); sta.pop();// 回溯 } if(!sta.empty()\u0026amp;\u0026amp;sta.top()==en[e]){ char k=sta.top(); sta.pop(); if(e+1!=lenen)dfs(s,e+1,op+\u0026#34;o \u0026#34;);// 不要问为什么这么写 else dfs(s,e+1,op+\u0026#34;o\u0026#34;);// 因为直接 dfs(s,e+1,op+\u0026#34;o \u0026#34;) 会 PE sta.push(k); } return; } int main(){ while(cin\u0026gt;\u0026gt;st\u0026gt;\u0026gt;en){ cout\u0026lt;\u0026lt;\u0026#34;[\\n\u0026#34;; string a=st,b=en; lenen=en.size(); sort(a.begin(),a.end()); sort(b.begin(),b.end()); if(a==b)dfs(0,0,\u0026#34;\u0026#34;);// 特判类似于样例中的 st=long,en=short 两个字符串字符不一样的情况 cout\u0026lt;\u0026lt;\u0026#34;]\\n\u0026#34;; } return 0; } ","date":"4 July 2025","externalUrl":null,"permalink":"/20250704120300.html/","section":"Posts","summary":"","title":"题解：UVA732 Anagrams by Stack","type":"posts"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/ac9tm4zg %}\n前言： # 本题数据范围不大，使用 Floyd 也可通过，但本人练习迪杰斯特拉，所以写的是迪杰斯特拉建反图的题解。\n题意： # 有 $n$ 个点，求有多少个点到 $e$ 点的最小花费小于等于 $t$。\n有多组数据。\n思路： # 可以发现起点有很多个，但是终点只有一个，我们可以考虑建反图，把终点看做反图起点，然后跑迪杰斯特拉得出终点到每个点的最小花费，然后把花费小于等于 $t$ 的点的个数加起来就好了。\n输出格式注意。\nUVA 的输出格式是谜，本题输出要求每次输出答案后换行，如果这不是最后一组数据，则需要再换一次行；但是如果是最后一组数据则只需要换一次行。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int T,n,e,t,m; int a,b; LL c; bool vis[120]; LL dis[120]; struct node{ int now;LL v; friend bool operator \u0026lt;(node x,node y){ return x.v\u0026gt;y.v; } }; struct nod{ int to; LL val; }; priority_queue\u0026lt;node\u0026gt;dl; vector\u0026lt;vector\u0026lt;nod\u0026gt; \u0026gt;tu; void dij(int s){ memset(dis,127,sizeof dis); memset(vis,0,sizeof vis); dl.push({s,0}); dis[s]=0; while(dl.empty()==0){ int now=dl.top().now; LL v=dl.top().v; dl.pop(); if(vis[now])continue; vis[now]=1; for(auto to:tu[now]){ if(dis[to.to]\u0026gt;dis[now]+to.val){ dis[to.to]=dis[now]+to.val; dl.push({to.to,dis[to.to]}); } } } return; } int main(){ ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin\u0026gt;\u0026gt;T; while(T--){ tu.clear(); tu.resize(120); cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;e\u0026gt;\u0026gt;t\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=m;i++){ cin\u0026gt;\u0026gt;a\u0026gt;\u0026gt;b\u0026gt;\u0026gt;c; tu[b].push_back({a,c}); } dij(e); int ans=0; for(int i=1;i\u0026lt;=n;i++){ if(dis[i]\u0026lt;=t)ans++; } cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; if(T)cout\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;// 一定要这样，不然 PE } return 0; } ","date":"1 July 2025","externalUrl":null,"permalink":"/20250701164700.html/","section":"Posts","summary":"","title":"题解：UVA1112 Mice and Maze","type":"posts"},{"content":"","date":"1 July 2025","externalUrl":null,"permalink":"/tags/%E6%9C%80%E7%9F%AD%E8%B7%AF/","section":"Tags","summary":"","title":"最短路","type":"tags"},{"content":"","date":"30 June 2025","externalUrl":null,"permalink":"/tags/%E9%AB%98%E7%B2%BE%E5%BA%A6/","section":"Tags","summary":"","title":"高精度","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/6ibipkf2 %}\n题意： # 输入 $n$ 行，每行一个分数，格式为 $p / q$（数字与字符中间有空格），你需要将分数简化到最简形式并按格式输出。\n其中 $1\\le p,q \\le 10^{30}$。\n思路： # 我们知道，一个分数的最简形式可以通过将分子和分母同时除以它们的最大公因数来获得。\n那么先不考虑数据范围，我们可以得到一个简单的代码：\n#include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n; LL p,q; int main(){ cin\u0026gt;\u0026gt;n; char k; while(n--){ cin\u0026gt;\u0026gt;p\u0026gt;\u0026gt;k\u0026gt;\u0026gt;q; LL gcd=__gcd(p,q); cout\u0026lt;\u0026lt;p/gcd\u0026lt;\u0026lt;\u0026#34; / \u0026#34;\u0026lt;\u0026lt;q/gcd\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } 那么加上数据范围呢？我们可以请出神奇的 __int128，它表示的范围大约是 $\\pm{10^{38}}$，实际范围是 $-2^{127} \\sim 2^{127} - 1$，对于我们的数据范围最大只有 $10^{30}$ 是足够的。\n特别注意 __int128 不能直接用 cin 输入和 cout 输出。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long __int128 p,q; int n; __int128 read(){ __int128 x=0; bool f=0; char c=getchar(); while(!(c\u0026gt;=\u0026#39;0\u0026#39;\u0026amp;\u0026amp;c\u0026lt;=\u0026#39;9\u0026#39;))c=getchar(),f=!f;// 因为输入中间有空格，要去除！ while(c\u0026gt;=\u0026#39;0\u0026#39;\u0026amp;\u0026amp;c\u0026lt;=\u0026#39;9\u0026#39;)x=x*10+c-\u0026#39;0\u0026#39;,c=getchar(); return x;// 本题保证输入为正数，所以直接返回 } void write(__int128 x){ if(x\u0026gt;0){// 本题保证 1\u0026lt;=p,q\u0026lt;=10^30 是正数，所以不考虑负数了 write(x/10); cout\u0026lt;\u0026lt;int(x%10); } return; } int main(){ cin\u0026gt;\u0026gt;n; char k; while(n--){ p=read(); cin\u0026gt;\u0026gt;k;// 中间的除号 q=read(); p=-p;q=-q; __int128 gcd=__gcd(p,q); write(p/gcd);cout\u0026lt;\u0026lt;\u0026#34; / \u0026#34;;write(q/gcd);cout\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } AC 记录。\n","date":"30 June 2025","externalUrl":null,"permalink":"/20250630135900.html/","section":"Posts","summary":"","title":"题解：UVA10814 Simplifying Fractions","type":"posts"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/7mjbtdss %}\n发现题解区的线段树题解都是建 $26$ 棵线段树的方法，来一个不建 $26$ 棵线段树的线段树题解。本题思路其实来源于上课老师讲了这题，并且用的是本题解的方法。\n思路： # 线段树。\n本题除了操作三以外是模版，本题解主要讲操作三，没学过线段树出门右转线段树模版。\n我们知道线段树把区间全部修改成同一个值是很简单的，但是本题操作三是要求排序，很可能是修改成不同的值，怎么办呢？我们可以想到既然是要求排序，那么排序后的值是不是连续的，既然是连续的，那是不是可以把它转换为一个普通的区间修改。\n考虑把要排序的范围里的所有字母的数量求出来，然后按顺序枚举每个字母，确定排序后被排到了哪个范围，然后正常区间修改就好了。\n如何确定被排序后在哪个范围内？首先设现在的起点为 $st$，有 $num$ 个这个字母，那么修改的这个区间大小也是 $num$，但是因为起点也算是占了区间的一个大小，所以终点是 $st+num-1$。\n完整代码： # // 本代码经过 devc++ 自带的格式化格式化 #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long struct node { int cnt[40];// 保存字母出现个数 int tag;// 懒标记标记要换成什么字母 int l,r; node() { l=r=0; tag=-1; memset(cnt,0,sizeof cnt); } } tree[(55000)*4]; int n,m; string s; int op; int x,y; char k; int cnt[40];// 保存字母出现个数，用于操作三，定义全局变量，函数内也可以访问 int szid(char s) { return toupper(s)-\u0026#39;A\u0026#39;; // 把字母转换为 cnt 内对应下标 } void push_up(int now) { for(int i=0; i\u0026lt;=27; i++) { tree[now].cnt[i]=tree[now*2].cnt[i]+tree[now*2+1].cnt[i]; } return; } void push_down(int now) { if(tree[now].tag!=-1) { memset(tree[now*2].cnt,0,sizeof tree[now*2].cnt); memset(tree[now*2+1].cnt,0,sizeof tree[now*2+1].cnt); tree[now*2+1].tag=tree[now*2].tag=tree[now].tag; tree[now*2+1].cnt[tree[now].tag]=tree[now*2+1].r-tree[now*2+1].l+1; tree[now*2].cnt[tree[now].tag]=tree[now*2].r-tree[now*2].l+1; tree[now].tag=-1; } } void build(int now,int l,int r) { if(l\u0026gt;r)return; tree[now].l=l; tree[now].r=r; if(l==r) { tree[now].cnt[szid(s[l])]++; return; } int mid=(l+r)\u0026gt;\u0026gt;1; build(now*2,l,mid); build(now*2+1,mid+1,r); push_up(now); return; } void query_cx(int now,int nl,int nr) { int l=tree[now].l,r=tree[now].r; if(nl\u0026lt;=l\u0026amp;\u0026amp;nr\u0026gt;=r) { for(int i=0; i\u0026lt;=27; i++) { cnt[i]+=tree[now].cnt[i]; } return; } push_down(now); int mid=(l+r)\u0026gt;\u0026gt;1; if(mid\u0026gt;=nl)query_cx(now*2,nl,nr);// l,mid if(mid\u0026lt;nr)query_cx(now*2+1,nl,nr);// mid+1 r return; } void change(int now,int nl,int nr,int k) { int l=tree[now].l,r=tree[now].r; if(nl\u0026lt;=l\u0026amp;\u0026amp;nr\u0026gt;=r) { memset(tree[now].cnt,0,sizeof tree[now].cnt); tree[now].cnt[k]=r-l+1; tree[now].tag=k; return; } push_down(now); int mid=(l+r)\u0026gt;\u0026gt;1; if(mid\u0026gt;=nl)change(now*2,nl,nr,k); if(mid\u0026lt;nr)change(now*2+1,nl,nr,k); push_up(now); return; } void change_px(int nl,int nr) { int start=nl; for(int i=0; i\u0026lt;=27; i++) { if(cnt[i]==0)continue; change(1,start,start+cnt[i]-1,i);// 确定修改范围，使用 change 函数 start+=cnt[i];// 更新起点 } return; } int query(int now,int nl,int nr,int k) { int l=tree[now].l,r=tree[now].r; if(nl\u0026lt;=l\u0026amp;\u0026amp;nr\u0026gt;=r) { return tree[now].cnt[k]; } push_down(now); int mid=(l+r)\u0026gt;\u0026gt;1; int ans=0; if(mid\u0026gt;=nl)ans+=query(now*2,nl,nr,k);// l,mid if(mid\u0026lt;nr)ans+=query(now*2+1,nl,nr,k);// mid+1 r return ans; } int main() { cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m\u0026gt;\u0026gt;s; s=\u0026#34; \u0026#34;+s; build(1,1,n); while(m--) { cin\u0026gt;\u0026gt;op; if(op==1) { cin\u0026gt;\u0026gt;x\u0026gt;\u0026gt;y\u0026gt;\u0026gt;k; cout\u0026lt;\u0026lt;query(1,x,y,szid(k))\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } else if(op==2) { cin\u0026gt;\u0026gt;x\u0026gt;\u0026gt;y\u0026gt;\u0026gt;k; change(1,x,y,szid(k)); } else { cin\u0026gt;\u0026gt;x\u0026gt;\u0026gt;y; memset(cnt,0,sizeof cnt); query_cx(1,x,y);// 查询每个字母出现的次数 change_px(x,y); } } return 0; } ","date":"6 June 2025","externalUrl":null,"permalink":"/20250606171000.html/","section":"Posts","summary":"","title":"题解：P2787 语文1（chin1）- 理理思维","type":"posts"},{"content":"","date":"6 June 2025","externalUrl":null,"permalink":"/tags/%E7%BA%BF%E6%AE%B5%E6%A0%91/","section":"Tags","summary":"","title":"线段树","type":"tags"},{"content":"","date":"5 May 2025","externalUrl":null,"permalink":"/tags/c++%E8%AF%AD%E6%B3%95%E9%A2%98/","section":"Tags","summary":"","title":"C++语法题","type":"tags"},{"content":"","date":"5 May 2025","externalUrl":null,"permalink":"/tags/%E5%88%86%E7%B1%BB%E8%AE%A8%E8%AE%BA/","section":"Tags","summary":"","title":"分类讨论","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/jfgn4oek %}\n思路： # 通过分析，可以发现：\n当 $n$ 和 $m$ 中有一个为 $1$ 时，只用一条线就可以，答案为 $1$。 当 $n$ 和 $m$ 中没有一个为 $1$ 时，当 $n","date":"5 May 2025","externalUrl":null,"permalink":"/20250505183900.html/","section":"Posts","summary":"","title":"题解：P12418 【MX-X12-T1】「ALFR Round 5」地铁","type":"posts"},{"content":"","date":"5 May 2025","externalUrl":null,"permalink":"/tags/%E5%8D%A1%E7%89%B9%E5%85%B0%E6%95%B0/","section":"Tags","summary":"","title":"卡特兰数","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/87ccj4j3 %}\n思路： # 设第一个士兵与第 $2k+1$ 个士兵配对（$k \\ge 0$），则：\n左侧：前 $2k$ 个士兵必须形成合法分组，方式数为 $C_k$。 右侧：剩余 $2(n-k-1)$ 个士兵形成合法分组，方式数为 $C_{n-k-1}$。 综上： $$C_n=\\sum_{k=0}^{n-1} C_k \\times C_{n-k-1} $$可以发现其实就是卡特兰数。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL T,n; LL C[5100]; const int Mod=1e9+7; int main(){ C[0]=1; for(int i=1;i\u0026lt;=5005;i++){ for(int k=0;k\u0026lt;i;k++){ C[i]+=C[k]*C[i-k-1]%Mod; C[i]%=Mod; } } cin\u0026gt;\u0026gt;T; while(T--){ cin\u0026gt;\u0026gt;n; cout\u0026lt;\u0026lt;C[n]\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } ","date":"5 May 2025","externalUrl":null,"permalink":"/20250505122800.html/","section":"Posts","summary":"","title":"题解：UVA12887 The Soldier's Dilemma","type":"posts"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/mdn6jl0j %}\n题意总结： # $7$ 个字母 W，H，Q，E，S，T，X 分别代表着 $1,\\dfrac{1}{2},\\dfrac{1}{4},\\dfrac{1}{8},\\dfrac{1}{16},\\dfrac{1}{32},\\dfrac{1}{64}$。\n每行一个字符串，分为好几节，每节用 / 分隔，求每个字符串有几个小节字母代表的值和为 $1$。\n思路： # 我不想处理小数，可以想到去分母，同乘分母最小公倍数 $64$，然后处理每个字符串，计算每小节的字母表示的数的和，如果这小节所有字母表示的数的和等于 $64$ 就让答案加一。\n注意：多测不清空，爆零两行泪。\n代码： # 用 map 存字符表示值： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long string s; unordered_map\u0026lt;char,int\u0026gt;mp; int main(){ mp[\u0026#39;W\u0026#39;]=64;// 不用小数同乘分母的最小公倍数 64 mp[\u0026#39;H\u0026#39;]=32;// 也就变成了判断一小节的和是否为 64 mp[\u0026#39;Q\u0026#39;]=16;// 是 ans++ mp[\u0026#39;E\u0026#39;]=8;// 否 ans 不变 mp[\u0026#39;S\u0026#39;]=4; mp[\u0026#39;T\u0026#39;]=2; mp[\u0026#39;X\u0026#39;]=1; while(getline(cin,s)){ if(s==\u0026#34;*\u0026#34;)break; LL ans=0,cnt=0;; for(int i=0;i\u0026lt;s.size();i++){ if(s[i]==\u0026#39;/\u0026#39;){ ans+=(cnt==64); cnt=0; } else{ cnt+=mp[s[i]]; } } cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } 使用 if 判断： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long string s; int main(){ while(getline(cin,s)){ if(s==\u0026#34;*\u0026#34;)break; LL ans=0,cnt=0;; for(int i=0;i\u0026lt;s.size();i++){ if(s[i]==\u0026#39;/\u0026#39;){ ans+=(cnt==64); cnt=0; } else{ if(s[i]==\u0026#39;W\u0026#39;)cnt+=64; else if(s[i]==\u0026#39;H\u0026#39;)cnt+=32; else if(s[i]==\u0026#39;Q\u0026#39;)cnt+=16; else if(s[i]==\u0026#39;E\u0026#39;)cnt+=8; else if(s[i]==\u0026#39;S\u0026#39;)cnt+=4; else if(s[i]==\u0026#39;T\u0026#39;)cnt+=2; else if(s[i]==\u0026#39;X\u0026#39;)cnt+=1; } } cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } 这是闲话，好像南昌音乐机考会有这种题。\n","date":"5 May 2025","externalUrl":null,"permalink":"/20250505115300.html/","section":"Posts","summary":"","title":"题解：UVA12195 叮当作曲","type":"posts"},{"content":"","date":"9 April 2025","externalUrl":null,"permalink":"/categories/atcoder/","section":"Categories","summary":"","title":"AtCoder","type":"categories"},{"content":"","date":"9 April 2025","externalUrl":null,"permalink":"/tags/%E5%93%88%E5%B8%8C/","section":"Tags","summary":"","title":"哈希","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/uev918iz %}\n思路： # 题目要求求两个集合中相同的元素个数除以两个集合合并在一起并去重后的元素个数。\n考虑使用 set。\n定义两个 set，第一个 set 存第一个集合中的元素，第二个 set 存两个集合的元素。\n定义一个变量做两个集合中相同元素的计数器。\n在输入时，把两个集合中的元素都放进第二个 set 里。\n在输入第二个集合时，顺便判断这个元素是否在第一个集合中出现过，出现过让计数器加一。\n最后输出两个集合中相同的元素个数除以两个集合合并在一起并去重后的元素个数就行了。\nset 会自动去重。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long set\u0026lt;string\u0026gt;a,all; string x; int n,m; int main(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=n;i++){ cin\u0026gt;\u0026gt;x; a.insert(x); all.insert(x); } int siz=0; for(int i=1;i\u0026lt;=m;i++){ cin\u0026gt;\u0026gt;x; all.insert(x); if(a.count(x))siz++; } cout\u0026lt;\u0026lt;double(siz)/all.size(); return 0;\t} ","date":"9 April 2025","externalUrl":null,"permalink":"/20250409201300.html/","section":"Posts","summary":"","title":"题解：AT_arc033_2 [ARC033B] メタ構文変数","type":"posts"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/z6668plu %}\n题意： # 将数组分割成两个非空连续子数组，使得两个子数组中不同元素的数量之和最大。\n思路： # 定义数组 $pre$，其中 $pre_i$ 表示前 $i$ 个元素中不同元素的数量。\n定义数组 $suf$，其中 $suf_i$ 表示从位置 $i$ 到末尾的不同元素数量。\n将两个数字预处理完成后，对于所有满足 $1 \\le i \u003c n$ 的 $pre_i+suf_{i+1}$ 中取最大值就是答案。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long const int N=3e5+5; int n,a[N],pre[N],suf[N]; int main(){ ios::sync_with_stdio(false); cin.tie(0); cin\u0026gt;\u0026gt;n; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;a[i]; unordered_set\u0026lt;int\u0026gt;spre; pre[0]=0; for(int i=1;i\u0026lt;=n;i++){ spre.insert(a[i]); pre[i]=spre.size(); } unordered_set\u0026lt;int\u0026gt;ssuf; suf[n+1]=0; for(int i=n;i\u0026gt;=1;i--){ ssuf.insert(a[i]); suf[i]=ssuf.size(); } int maxx=0; for(int i=1;i\u0026lt;n;i++){ maxx=max(maxx,pre[i]+suf[i+1]); } cout\u0026lt;\u0026lt;maxx; return 0; } 提交记录。\n","date":"16 March 2025","externalUrl":null,"permalink":"/20250316083100.html/","section":"Posts","summary":"","title":"题解：AT_abc397_c [ABC397C] Variety Split Easy","type":"posts"},{"content":"","date":"16 March 2025","externalUrl":null,"permalink":"/tags/%E9%A2%84%E5%A4%84%E7%90%86/","section":"Tags","summary":"","title":"预处理","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/d7uchu06 %}\n题意： # 将数组分割成三个非空连续子数组，使得三个子数组中不同元素的数量之和最大。\n思路： # 线段树。\n可以先把线段树的加法模版做了在来看。\n定义数组 $pre$，其中 $pre_i$ 表示前 $i$ 个元素中不同元素的数量。\n定义数组 $suf$，其中 $suf_i$ 表示从位置 $i$ 到末尾的不同元素数量。\n这样就可以差不多可以把这道题的弱化版，本场的 C 题写出来。\n接下来继续考虑本题。\n定义 $mid(i,j)$ 是对于 $A$ 数组中的所有满足下标 $i \\le k \\le j$ 的 $A_k$ 的不同元素个数。\n用数学方式可以表示为 $mid(i, j)= \\left| \\{ A_k \\mid i \\leq k \\leq j \\} \\right|$。\n对于每个分割点 $j$（第二个分割点的位置），我们需要找到最优的第一个分割点 $i$，使得 $pre_i+mid(i+1,j)+suf_{j+1}$ 的值最大。\n我们可以使用线段树维护每个位置 $i$ 的 $pre_i+mid(i+1,j)$ 的最大值。\n在维护中间段时，当 $A_j$ 出现重复时，只有 $i \\ge l$ 的分割点会使中间段包含新的 $A_j$，因此要让区间 $[l,j−1]$ 维护的值加一。\n定义一个变量 $maxx$ 用来存答案。\n对每个 $j$，查询线段树维护的区间 $[1,j−1]$ 的值，加上 $suf_{j+1}$ 后更新全局最大值。\n总时间复杂度 $O(N \\log N)$。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long const int N=3e5+5; int n,a[N*4],pre[N*4],suf[N*4],las[N*4]; vector\u0026lt;int\u0026gt;value(N*4),lazy(N*4); void build(int now,int l,int r){ if(l==r){ value[now]=pre[l]; return;// 别忘了 return 赛时 15 分钟打忘记加喜提 RE } int mid=l+(r-l)/2; build(2*now,l,mid); build(2*now+1,mid+1,r); value[now]=max(value[now*2],value[now*2+1]); } void push(int now){// 懒标记 if(lazy[now]==0)return; value[2*now]+=lazy[now]; value[2*now+1]+=lazy[now]; lazy[2*now]+=lazy[now]; lazy[2*now+1]+=lazy[now]; lazy[now]=0; } void change(int now,int l,int r,int ql,int qr,int val){ if(qr\u0026lt;l||ql\u0026gt;r)return; if(ql\u0026lt;=l\u0026amp;\u0026amp;r\u0026lt;=qr){ value[now]+=val; lazy[now]+=val; return; } push(now); int mid=l+(r-l)/2; change(2*now,l,mid,ql,qr,val); change(2*now+1,mid+1,r,ql,qr,val); value[now]=max(value[2*now],value[now*2+1]); } int ans(int now,int l,int r,int ql,int qr){ if(qr\u0026lt;l||ql\u0026gt;r)return 0; if(ql\u0026lt;=l\u0026amp;\u0026amp;r\u0026lt;=qr){ return value[now]; } push(now); int mid=l+(r-l)/2; return max(ans(now*2,l,mid,ql,qr),ans(now*2+1,mid+1,r,ql,qr)); } int main(){ ios::sync_with_stdio(false); cin.tie(0); cin\u0026gt;\u0026gt;n; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;a[i]; unordered_set\u0026lt;int\u0026gt;spre; pre[0]=0; for(int i=1;i\u0026lt;=n;i++){// 处理前 i 个元素中不同元素的数量 spre.insert(a[i]); pre[i]=spre.size(); } unordered_set\u0026lt;int\u0026gt;ssuf; suf[n+1]=0; for(int i=n;i\u0026gt;=1;i--){// 处理从位置 i 到末尾的不同元素数量。 ssuf.insert(a[i]); suf[i]=ssuf.size(); } build(1,1,n-1);// 建树 int maxx=0; for(int j=1;j\u0026lt;=n-1;j++){ int x=a[j]; int l=las[x]; int le=max(l,1); int r=j-1; if(l\u0026lt;=r){ change(1,1,n-1,le,r,1); } las[x]=j; if(j\u0026gt;=2){ int cm=ans(1,1,n-1,1,j-1); int cs=cm+suf[j+1]; maxx=max(maxx,cs); } } cout\u0026lt;\u0026lt;maxx; return 0; } 提交记录。\n","date":"15 March 2025","externalUrl":null,"permalink":"/20250315223300.html/","section":"Posts","summary":"","title":"题解：AT_abc397_f Variety Split Hard","type":"posts"},{"content":"{% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/1d8jrjht %}\n题意： # 给定一个有向图，我们从顶点 $1$ 出发，想要到达顶点 $N$。\n有两种操作：\n沿着有向边移动到下一个顶点，花费为 $1$。 反转所有边的方向，花费为 $X$。 我们的目标是找到到达 $N$ 的最小总花费。\n思路： # 最短路，迪杰斯特拉。\n只需要改建图就行了。\n建图： # 每个顶点 $u$ 在原图和反转图中被分别表示为两个节点 $2 \\times u$ 为状态 $0$ 原图和 $2 \\times u+1$ 为状态 $1$ 反转图。\n原图边：对于原边 $u$ 到 $v$，在状态 $0$ 中添加边 $2 \\times u$ 到 $2 \\times v$ 代价为 $1$。\n反转边：对于原边 $u$ 到 $v$，在状态 $1$ 中添加边 $2 \\times v+1$ 到 $2 \\times u+1$ 代价为 $1$。\n状态切换边：每个顶点 $u$ 在两种状态间添加 $2 \\times u$ 到 $2 \\times u + 1$ 的双向边，代价为 $X$。\n之后照常跑迪杰斯特拉就行了。\n因为每个点都被分成了两个，所以最后输出要在两个终点中取最小值。\n提交记录和代码。\n","date":"1 March 2025","externalUrl":null,"permalink":"/20250301224400.html/","section":"Posts","summary":"","title":"题解：abc395_e E - Flip Edge","type":"posts"},{"content":"{% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/q1jz2m4o %}\n翻译： # 高桥君有 $2 \\times N$ 个牙齿，$N$ 个上排的牙齿和 $N$ 个下排的牙齿。\n注：以下为了方便“上牙”指的是上排的牙齿，“下牙”指的是下排的牙齿。\n第 $i$ 个上牙的长度为 $U_i$，第 $i$ 个下牙的长度为 $D_i$。\n有个操作，需要花费一元，选择一个长度大于 $0$ 的牙齿，将其长度减少 $1$。\n求使牙齿“咬合良好”的最小总费用。\n牙齿“咬合良好”需要满足以下两个条件：\n存在一个整数 $H$，使得对于所有 $1 \\le i \\le N$，满足 $U_i + D_i$ 等于 $H$。 对于所有 $1 \\le i \u003c N$，满足 $\\left |U_i - U_{i+1} \\right | \\le X$。 思路： # 二分搜索。\n二分寻找符合条件的最大的 $H$。\n找到后计算最小费用就行了，设总费用为 $ans$，则 $$ans=\\sum_{i=1}^{N}(U_i+D_i-H)$$ 二分查找部分： # 为什么可以二分 $H$ 和二分范围： # 我们可以发现如果某一个 $H$ 是可行的，因为我们可以使用操作去减少牙齿的长度，所以所有比 $H$ 小的值也可能是可行的，所以我们可以发现 $H$ 是满足单调性的。\n$H$ 的左边界是 $0$，因为所有牙齿的长度都为 $0$ 时一定是满足条件的；$H$ 的右边界是所有 $U_i+D_i$ 的最小值，因为需要满足 $U_i + D_i$ 等于 $H$，而我们的操作只能减少长度，不能增加长度。\n查询是否符合条件： # 定义两个数组 $a$ 和 $b$，$a_i$ 是第 $i$ 个上牙的最小可能长度，$b_i$ 是第 $i$ 个上牙的最大可能长度。\n因为必须满足 $U_i + D_i$ 等于 $H$，所以我们可以得出 $U_i$ 必须满足 $U_i=H-D_i$，由于长度至少要是 $0$，所以 $a_i=\\max(0,H-D_i)$。\n因为第 $i$ 个上牙的长度只有 $U_i$，而在第 $i$ 个下牙为 $0$ 时，要满足条件长度应该等于 $H$，所以 $b_i=\\min(U_i,H)$。\n先判断第一个上牙的范围是否合法，也就是左端点需要小于等于右端点，因为不可能会出现最大值小于最小值。\n接着循环除了第一个牙齿外的其它牙齿，检查相邻上牙的差值是否满足条件。\n如果所有上牙都满足条件，返回真，否则返回假。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL T=1,n,m,x; LL u[int(2*1e5+10)],d[int(2*1e5+10)]; LL al[int(2*1e5+10)],bl[int(2*1e5+10)]; vector\u0026lt;LL\u0026gt;sum_ud; bool check(LL mid){ for(int i=0;i\u0026lt;n;i++){ al[i]=max(0ll,mid-d[i]); bl[i]=min(u[i],mid); } LL cl=al[0],cr=bl[0]; if(cl\u0026gt;cr)return 0; for(int i=1;i\u0026lt;n;i++){ LL pl=cl,pr=cr; LL nl=max(al[i],pl-x),nr=min(bl[i],pr+x); if(nl\u0026gt;nr)return 0; cl=nl; cr=nr; } return 1; } int main(){ ios_base::sync_with_stdio(false); cin.tie(nullptr); while(T--){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;x; sum_ud.resize(n); for(int i=0;i\u0026lt;n;i++){ cin\u0026gt;\u0026gt;u[i]\u0026gt;\u0026gt;d[i]; sum_ud[i]=u[i]+d[i]; } LL hm=*min_element(sum_ud.begin(),sum_ud.end()); int l=0,r=hm,anss=0; while(l\u0026lt;=r){ LL mid=l+(r-l)/2; if(check(mid)){ anss=mid;l=mid+1; } else r=mid-1; } LL ans=0; for(int i=0;i\u0026lt;n;i++){ ans+=sum_ud[i]-anss; } cout\u0026lt;\u0026lt;ans; } return 0; } ","date":"1 March 2025","externalUrl":null,"permalink":"/20250301220500.html/","section":"Posts","summary":"","title":"题解：abc395_f F - Smooth Occlusion","type":"posts"},{"content":"{% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/ejbmn02f %}\n思路： # 对于第 $i$ 个人他看着的人是 $P_i$，而 $P_i$ 头上戴着的帽子编号是 $Q_{P_i}$。\n定义一个 $ans$ 数组，其中 $ans_i$ 表示头上戴着的帽子编号为 $i$ 的人他看着的人头上戴着的帽子的编号。\n第 $i$ 个人戴着的帽子编号是 $Q_i$，而我们又知道 $P_i$ 头上戴着的帽子编号是 $Q_{P_i}$，所以 $ans_{Q_i}$ 会等于 $Q_{P_i}$。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL N; LL P[int(3*1e5+15)],Q[int(3*1e5+15)]; LL ans[int(3*1e5+15)]; int main(){ cin\u0026gt;\u0026gt;N; for(int i=1;i\u0026lt;=N;i++){ cin\u0026gt;\u0026gt;P[i]; } for(int i=1;i\u0026lt;=N;i++){ cin\u0026gt;\u0026gt;Q[i]; } for(int i=1;i\u0026lt;=N;i++){ ans[Q[i]]=Q[P[i]]; } for(int i=1;i\u0026lt;=N;i++)cout\u0026lt;\u0026lt;ans[i]\u0026lt;\u0026lt;\u0026#34; \u0026#34;; return 0; } ","date":"10 February 2025","externalUrl":null,"permalink":"/20250210112600.html/","section":"Posts","summary":"","title":"题解：AT_abc392_c [ABC392C] Bib","type":"posts"},{"content":"{% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/pxcoa4ou %}\n思路： # 考虑到题目要求我们求的是出现相同数字的概率，所以我们可以使用 unordered_map 把每一个骰子中出现的数字的数量存下来。\n然后进行循环枚举我们使用的两个骰子，注意到使用编号为 $1$ 和 $2$ 的骰子和使用编号为 $2$ 和 $1$ 的骰子的出现相同数字的概率其实是一样的，所以我们枚举使用骰子的时候可以通过让第二个骰子的编号 $j$ 大于我们第一个骰子的编号 $i$ 来减少循环。\n在枚举两个 unordered_map 中是否有相同的数时，也可以进行优化，我们可以枚举 unordered_map 中数字少的 unordered_map 中的数字，判断另外一个有没有相同的数字。\n注意要开 long long。\n浮点数用 double 就可以了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn LL #define ull unsigned long long LL n,k[110]; vector\u0026lt;unordered_map\u0026lt;LL,LL\u0026gt; \u0026gt;a(1); int main(){ //\tios::sync_with_stdio(0); //\tcin.tie(0);cout.tie(0); cin\u0026gt;\u0026gt;n; for(LL i=1;i\u0026lt;=n;i++){ cin\u0026gt;\u0026gt;k[i]; unordered_map\u0026lt;LL,LL\u0026gt;temp; for(LL j=1;j\u0026lt;=k[i];j++){ LL x;cin\u0026gt;\u0026gt;x; temp[x]++; } a.push_back(temp); } double ans=0.0; for(LL i=1;i\u0026lt;=n;i++){ for(LL j=i+1;j\u0026lt;=n;j++){ auto mp1=a[i],mp2=a[j]; long long sum=0; if(mp1.size()\u0026lt;=mp2.size()){ for(auto x:mp1){ auto it=mp2.find(x.first); if(it!=mp2.end()){ sum+=x.second*it-\u0026gt;second; } } } else{ for(auto x:mp2){ auto it=mp1.find(x.first); if(it!=mp1.end()){ sum+=x.second*it-\u0026gt;second; } } } double tamp=((double)(sum))/(k[i]*k[j]); if(tamp\u0026gt;ans)ans=tamp; } } cout\u0026lt;\u0026lt;fixed\u0026lt;\u0026lt;setprecision(15)\u0026lt;\u0026lt;ans; return 0; } ","date":"9 February 2025","externalUrl":null,"permalink":"/20250209083300.html/","section":"Posts","summary":"","title":"题解：AT_abc392_d [ABC392D] Doubles","type":"posts"},{"content":"","date":"3 February 2025","externalUrl":null,"permalink":"/tags/%E5%88%86%E6%94%AF%E7%BB%93%E6%9E%84/","section":"Tags","summary":"","title":"分支结构","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/qufx1erv %}\n题意： # 给定 $N$ 个袋子，每个袋子里有若干石子。每次操作可以选择两个袋子，将其中一个的所有石子倒入另一个。求所有操作结束后，各袋子石子数的异或和有多少种不同的可能值。\n思路： # 观察发现最终异或和的值仅与分组方式有关。每次合并操作相当于将两个袋子的石子合并为一个新的袋子，最终的分组可以看作将初始的 $N$ 个袋子划分成若干非空子集，每个子集的石子总和构成最终的异或和。\n可以通过动态规划逐步生成所有可能的分组方式。\n处理每个石子堆时，对当前所有可能的状态进行扩展：\n新增分组，将当前石子堆作为独立分组。 合并到已有分组，将当前石子堆合并到任意一个已有分组中。 最后进行去重统计。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n; vector\u0026lt;LL\u0026gt;A(20); vector\u0026lt;pair\u0026lt;vector\u0026lt;LL\u0026gt;,LL\u0026gt; \u0026gt;cur; unordered_set\u0026lt;LL\u0026gt;ans; int main(){ cin\u0026gt;\u0026gt;n; for(int i=0;i\u0026lt;n;i++)cin\u0026gt;\u0026gt;A[i]; cur.emplace_back(vector\u0026lt;LL\u0026gt;{A[0]},A[0]); for(int i=1;i\u0026lt;n;i++){ vector\u0026lt;pair\u0026lt;vector\u0026lt;LL\u0026gt;,LL\u0026gt; \u0026gt;tamp; for(auto g:cur){ vector\u0026lt;LL\u0026gt;zhu=g.first; LL v=g.second; // 新增分组 vector\u0026lt;LL\u0026gt;newg=zhu; newg.emplace_back(A[i]); LL nv=v^A[i]; tamp.emplace_back(newg,nv); // 合并到已有分组 for(int j=0;j\u0026lt;zhu.size();j++){ vector\u0026lt;LL\u0026gt;temp(zhu); temp[j]+=A[i]; LL val=v^zhu[j]^temp[j]; tamp.emplace_back(temp,val); } } cur=tamp; } for(auto g:cur){ LL v=g.second; ans.insert(v); } cout\u0026lt;\u0026lt;ans.size(); return 0; } AC 记录。\n","date":"3 February 2025","externalUrl":null,"permalink":"/20250203121400.html/","section":"Posts","summary":"","title":"题解：AT_abc390_d [ABC390D] Stone XOR","type":"posts"},{"content":"","date":"3 February 2025","externalUrl":null,"permalink":"/tags/%E5%BE%AA%E7%8E%AF%E7%BB%93%E6%9E%84/","section":"Tags","summary":"","title":"循环结构","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/g7onnsui %}\n思路： # 语法题。\n考点：循环结构和分支结构。\n将两个批改结果的和分别存下来，如果第一次的和大于等于 $T$，则输出第二次批改结果的和，否则输出第一次批改结果的和。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int T=1; int n; int a[15],b[15]; int main(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;T; LL sum1=0,sum2=0; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;a[i],sum1+=a[i]; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;b[i],sum2+=b[i]; if(sum1\u0026gt;=T)cout\u0026lt;\u0026lt;sum2\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; else cout\u0026lt;\u0026lt;sum1\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; return 0; } ","date":"31 January 2025","externalUrl":null,"permalink":"/20250131182300.html/","section":"Posts","summary":"","title":"题解：P11641 【MX-X8-T0】「TAOI-3」分数","type":"posts"},{"content":"","date":"26 January 2025","externalUrl":null,"permalink":"/categories/noip/","section":"Categories","summary":"","title":"NOIP","type":"categories"},{"content":"","date":"26 January 2025","externalUrl":null,"permalink":"/tags/%E4%BA%8C%E5%8F%89%E6%A0%91/","section":"Tags","summary":"","title":"二叉树","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/gvn23fp6 %}\n思路： # 递归构建二叉树，并输出先序排列。\n首先要知道后序排列、中序排列是怎么构成的。\n后序排列是先左，然后右，最后根；中序排列是先左，然后根，最后右。\n依此我们可以确定后序排列中的最后一个一定是这个树的根，我们可以将根的左右两边分为两个子树，左子树和右子树，在中序排列中在根左边的是左子树，在右边的是右子树，最后便可构建出二叉树。\n例如样例：\n给出了中序排列为 BADC，后序排列为 BDCA，可以看的后序排列的最后一个字母是 A，那么说明 A 是根，然后我们在中序排列中找到 A，此时 A 左边的是左子树里的，右边是右子树里的。\n所以现在可以转换为：\n左子树：中序排列为 B，后序排列是 B。 右子树：中序排列为 DC，后序排列是 DC。 接着我们一直递归直到不能递归就能构建出二叉树了。\n样例可能有点小不好理解这里给出另一组再进行一次解释。\n当中序排列为 CBEAGDF，后序排列为 CEBGFDA 时。\n后序排列的最后是 A，所以 A 是根，然后再确定左、右子树。\n然后当有左子树时，把它当做一个独立的二叉树处理，查询它的后序排列，确定根，然后接着确定这个二叉树的左、右子树；当有右子树时进行一样的操作。\n本树构建过程画出如下的图。\n当然在代码中我们无法同时处理左、右两个子树，所以在代码中我们要将它们分别处理。\n总的来说就是由后序排列确定根，由中序排列确定左子树和右子树，然后继续递归直到不能递归。\n为了输出先序排序，我们可以先把每次的根输出，然后处理左子树，最后再处理右子树，因为先序排序是先根，然后左，最后右。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long string a,b; struct node{ char data; int l,r; }tree[60]; void jiangshu(int al,int ar,int bl,int br,int k){ int root=a.find(b[br]); tree[k].data=b[br]; cout\u0026lt;\u0026lt;tree[k].data; if(root\u0026gt;al){ tree[k].l=2*k; jiangshu(al,root-1,bl,br-ar+root-1,2*k); } if(root\u0026lt;ar){ tree[k].r=2*k+1; jiangshu(root+1,ar,br+root-al,br-1,2*k+1); } } int main(){ cin\u0026gt;\u0026gt;a\u0026gt;\u0026gt;b; jiangshu(0,a.size()-1,0,b.size()-1,1); return 0; } ","date":"26 January 2025","externalUrl":null,"permalink":"/20250126213700.html/","section":"Posts","summary":"","title":"题解：P1030 [NOIP2001 普及组] 求先序排列","type":"posts"},{"content":"","date":"15 January 2025","externalUrl":null,"permalink":"/tags/%E8%83%8C%E5%8C%85-dp/","section":"Tags","summary":"","title":"背包 DP","type":"tags"},{"content":"","date":"15 January 2025","externalUrl":null,"permalink":"/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/","section":"Tags","summary":"","title":"动态规划","type":"tags"},{"content":" {% link 洛谷文章链接,xyx404,https://www.luogu.com.cn/article/y9l0giqv %}\n思路： # {% link 参考了 OI Wiki, ,https://oi-wiki.org/dp/knapsack/ %}\n背包 DP 模版题。\n首先定义一个 $dp$ 数组，其中 $dp_{i,j}$ 表示第 $i$ 个物品在背包容量为 $j$ 时的最大价值。\n考虑转移。\n枚举每一个物品在背包容量剩余 $j$ 时可以得到的最大价值。\n如果现在枚举的剩余容量 $j$ 小于选择这个物品需要的容量，那么 $dp_{i,j}$ 的最大值就是 $dp_{i-1,j}$。\n否则有两种情况，选和不选。\n对于选的情况，背包的剩余容量会减小这个物品需要的容量，价值会增加这个物品的价值，故此情况下的价值为 $dp_{i-1,j-uset_i}+price_i$；对于不选的情况，这个物品不会放进背包，所以和剩余容量不够的情况是一样的是 $dp_{i-1,j}$。\n在此处再解释下 $dp_{i-1,j-uset_i}+price_i$，这个时候需要选择第 $i$ 件物品，因为第 $i$ 件物品需要的空间是 $uset_i$ 枚举的背包容量等于 $j$，所以只剩下 $j-uset_i$ 空间就是留给前 $i-1$ 件物品，然后 $dp_{i-1,j-uset_i}$ 是第 $i-1$ 件物品，背包容量剩余 $j-uset_i$ 时的最大值，而现在我们又选择了第 $i$ 件物品，所以现在的价值是 $dp_{i-1,j-uset_i}+price_i$。\n所以可以得出转移方程：\n对于剩余容量大于等于选择这个物品需要的容量时\n$$dp_{i,j}=\\max(dp_{i-1,j-uset_i}+price_i,dp_{i-1,j})$$否则\n$$dp_{i,j}=dp_{i-1,j}$$ 代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int m,n,t; int dp[1050][1050]; int uset[105],price[105]; int main(){ cin\u0026gt;\u0026gt;t\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=m;i++) cin\u0026gt;\u0026gt;uset[i]\u0026gt;\u0026gt;price[i]; for(int i=1;i\u0026lt;=m;i++) for(int j=0;j\u0026lt;=t;j++){ if(j\u0026gt;=uset[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-uset[i]]+price[i]); else dp[i][j]=dp[i-1][j]; } cout\u0026lt;\u0026lt;dp[m][t]; return 0; } ","date":"15 January 2025","externalUrl":null,"permalink":"/20250115100500.html/","section":"Posts","summary":"","title":"题解：P1048 [NOIP2005 普及组] 采药","type":"posts"},{"content":"","date":"12 January 2025","externalUrl":null,"permalink":"/tags/%E6%A8%A1%E6%8B%9F/","section":"Tags","summary":"","title":"模拟","type":"tags"},{"content":" 洛谷文章链接\n思路： # 模拟。\n定义一个变量 $give$ 表示现在可以给珠子的人数，再定义一个数组 $R$，$R_i$ 表示有多少人只能给到第 $i$ 个人。\n每一个成年的人在自己还要珠子的时候必须要给刚刚成年的人一个珠子，所以第 $i$ 个人要给 $n-i$ 个珠子，可以得到 $give$ 个珠子，如果第 $i$ 个人的数量加上之前成年的人给他的数量不够给后面所有的刚成年的，那么计算他能给到第几个人，更新 $R$ 数组，并让 $give$ 加一还要记得把 $A_i$ 修改为 $0$；如果够那么 $A_i$ 就等于他加上 $give$ 后给减去他成年后还有多少个未成年的数量，$give$ 也要加一。\n更新完第 $i$ 个人后让 $give$ 减去只能给到第 $i$ 个人的数量，也就是 $R_i$。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL T=1; LL n; LL a[int(5*1e5+10)]; LL R[int(5*1e5+10)]; int main(){ //\tcin\u0026gt;\u0026gt;T; while(T--){ cin\u0026gt;\u0026gt;n; int give=0; for(int i=1;i\u0026lt;=n;i++){ cin\u0026gt;\u0026gt;a[i]; //\tcout\u0026lt;\u0026lt;give\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; if(a[i]-(n-i)+give\u0026lt;0){ R[a[i]+give+i]++; a[i]=0; } else{ a[i]=a[i]-(n-i)+give; } give++; give-=R[i]; cout\u0026lt;\u0026lt;a[i]\u0026lt;\u0026lt;\u0026#34; \u0026#34;; } } return 0; } ","date":"12 January 2025","externalUrl":null,"permalink":"/20250112100700.html/","section":"Posts","summary":"","title":"题解：AT_abc388_d [ABC388D] Coming of Age Celebration","type":"posts"},{"content":"","date":"5 January 2025","externalUrl":null,"permalink":"/tags/bfs/","section":"Tags","summary":"","title":"Bfs","type":"tags"},{"content":"洛谷文章链接\n思路： # BFS。\n如没学过，可以先看看 OI Wiki，然后把模版题 P1443 马的遍历做了。\n考虑到题目要求，上一次横着走，那下一次就必须竖着走；上一次竖着走，那下一次就必须横着走，所以 BFS 的队列里的变量要多一个变量 $cx$ 表示上一次走的方向，同时考虑到可能横着走到达这个点不可以到达终点，但是竖着走到达这个点可能可以到达终点，所以标记数组也要多一维，表示方向，当现在到达的点为终点时，说明可以到达输出答案。\n其余的就是 BFS 的模版了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int h,w; int sx,sy,ex,ey; bool bj[1200][1200][4]; char jz[1200][1200]; struct node{ int x,y; int cx;// 上一次的上下/左右/起点 int step; }; queue\u0026lt;node\u0026gt;dl; int dx[]={1,-1,0,0}; int dy[]={0,0,1,-1}; int main(){ cin\u0026gt;\u0026gt;h\u0026gt;\u0026gt;w; for(int i=1;i\u0026lt;=h;i++)for(int j=1;j\u0026lt;=w;j++){ cin\u0026gt;\u0026gt;jz[i][j]; if(jz[i][j]==\u0026#39;S\u0026#39;)sx=i,sy=j; else if(jz[i][j]==\u0026#39;G\u0026#39;)ex=i,ey=j; else if(jz[i][j]==\u0026#39;#\u0026#39;)bj[i][j][1]=1,bj[i][j][2]=1,bj[i][j][0]=1; } dl.push({sx,sy,0,0}); while(dl.empty()==0){ node tamp=dl.front();dl.pop(); //\tcout\u0026lt;\u0026lt;tamp.x\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;tamp.y\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; if(tamp.x==ex\u0026amp;\u0026amp;tamp.y==ey){ cout\u0026lt;\u0026lt;tamp.step; return 0; } if(bj[tamp.x][tamp.y][tamp.cx])continue; bj[tamp.x][tamp.y][tamp.cx]=1; for(int i=0;i\u0026lt;4;i++){ int tx=tamp.x+dx[i],ty=tamp.y+dy[i]; if(tx\u0026lt;1||ty\u0026lt;1||tx\u0026gt;h||ty\u0026gt;w)continue; if(i\u0026lt;=1\u0026amp;\u0026amp;tamp.cx!=1){ dl.push({tx,ty,1,tamp.step+1}); } else if(i\u0026gt;1\u0026amp;\u0026amp;tamp.cx!=2){ dl.push({tx,ty,2,tamp.step+1}); } } } cout\u0026lt;\u0026lt;-1; return 0; } AC 记录。\n","date":"5 January 2025","externalUrl":null,"permalink":"/20250105200000.html/","section":"Posts","summary":"","title":"题解：ABC387 D - Snaky Walk","type":"posts"},{"content":"","date":"31 December 2024","externalUrl":null,"permalink":"/tags/%E4%BA%8C%E5%88%86/","section":"Tags","summary":"","title":"二分","type":"tags"},{"content":"题目\n思路： # 二分答案。\n要求找最小值最大，所以考虑二分。\n对于每次二分的 $mid$ 最小需要跳跃的距离，进行一次检查，对于检查函数，为了确定编号为 $i$ 的岩石之前没有被删除的岩石是哪个，要定义一个 $last$ 存上一个没有被删除的岩石的编号，每次对比 $D_i$ 和 $D_{last}$ 的差，如果小于我们二分到的距离就说明要移走，否则不要移走更新 $last$ 为 $i$，如果要移走的岩石小于等于最多可以移走的岩石 $m$ 说明这种情况是可能的，否则不可能。\n答案为每个满足的 $mid$ 中的最大值。\n注意起点和终点要在数组里，$D$ 数组输入时不会有终点和起点，自行要添加。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; #define LL long long using namespace std; LL l,n,m; LL a[50003],maxx; bool check(LL mid){ int last=0,del=0; for(int i=1;i\u0026lt;=n;i++){ if(a[i]-a[last]\u0026lt;mid){ del++; } else last=i; } return del\u0026lt;=m; } int main(){ cin\u0026gt;\u0026gt;l\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;a[i];// 代码中的数组 a 为题目中的数组 D sort(a+1,a+1+n); a[++n]=l; LL r=l,l=1,mid; while(l\u0026lt;=r){ mid=(l+r)/2; if(check(mid)){ l=mid+1; maxx=mid; } else r=mid-1; } cout\u0026lt;\u0026lt;maxx; return 0; } ","date":"31 December 2024","externalUrl":null,"permalink":"/20241231175000.html/","section":"Posts","summary":"","title":"题解：P2678 [NOIP2015 提高组] 跳石头","type":"posts"},{"content":" 题目洛谷 # 题目 AtCoder # 思路： # 动态规划。\n计算出最小编辑距离，并检查其是否小于等于 $K$。\n二维动态规划： # 现在先考虑二维 $dp$ 数组的情况。\n二维 $dp$ 数组时，$dp_{i,j}$ 表示将 $S$ 的前 $i$ 个字符转换为 $T$ 的前 $j$ 个字符所需的最小操作数。\n然后考虑转移。\n在动态规划中，$dp_{i,j}$ 表示将字符串 $S$ 的前 $i$ 个字符转换为字符串 $T$ 的前 $j$ 个字符所需的最小操作数。为了计算 $dp_{i,j}$，我们需要考虑三种可能的操作：删除、插入和替换。\n具体来说：\n删除：如果我们从 $S$ 中删除一个字符，那么 $dp_{i,j}$ 可以由 $dp_{i-1,j}$ 转移而来，因为删除一个字符后，$S$ 的前 $i-1$ 个字符需要转换为 $T$ 的前 $j$ 个字符，这一步对应的操作次数加 $1$。 插入：如果我们向 $S$ 插入一个字符，使得它与 $T$ 的第 $j$ 个字符匹配，那么 $dp_{i,j}$ 可以由 $dp_{i,j-1}$ 转移而来，因为插入一个字符后，$S$ 的前 $i$ 个字符需要转换为 $T$ 的前 $j-1$ 个字符，这一步对应的操作次数加 $1$。 替换：如果我们用 $T$ 的第 $j$ 个字符替换 $S$ 的第 $i$ 个字符，那么 $dp_{i,j}$ 可以由 $dp_{i-1,j-1}$ 转移而来，因为替换一个字符后，$S$ 的前 $i-1$ 个字符需要转换为 $T$ 的前 $j-1$ 个字符。如果 $S_{i-1}$ 已经等于 $T_{j-1}$，则不需要额外增加操作次数；否则，这一步对应的操作次数加 $1$。 因此，$dp_{i,j}$ 的值是上述三种情况中的最小值。\n同时当两个字符串的长度差大于 $K$ 时，一定是不可能在 $K$ 步内让他们相同的。\n现在写出的代码可以解决一些数据小的测试点了，但是数据大一点的话，二维 $dp$ 数组会炸内存，因此考虑优化内存。\n优化为一维： # 观察状态转移方程可以发现，计算 $dp_{i,j}$ 只需要用到 $dp_{i-1,j}$、$dp_{i,j-1}$ 和 $dp_{i-1,j-1}$ 这三个值。这意味着我们并不需要整个二维数组来存储所有的中间结果，只需要当前行和上一行的结果即可。\n具体的：\n定义两个一维数组 $dp$ 和 $dp2$，$dp2$ 是上一行的值，数组大小为 $m+1$。\n优化成一维后转移方程也要发生改变。\n只需要将使用了上一行数据的改为访问 $dp2$ 的就行了，举个例子，对于二维时的 $dp_{i-1,j}$ 现在应该访问 $dp2_{j}$。\n对应没有访问上一行数据的直接访问 $dp$ 数组就行了，举个例子，对于二维时的 $dp_{i,j-1}$ 现在应该访问 $dp_{j-1}$。\n为了防止转移错误，我们要保证 $m \\le n$，如果当 $m \u003e n$ 时，会导致初始化错误，注意这是在数组大小为 $m+1$ 的情况下。\n现在代码如下：\n#include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL k; string S,T; //vector\u0026lt;vector\u0026lt;int\u0026gt; \u0026gt;dp(500050,vector\u0026lt;int\u0026gt;(500050)); int dp[500050]; void solve(string S,string T){ int n=T.size(),m=S.size(); //\tvector\u0026lt;vector\u0026lt;int\u0026gt; \u0026gt;dp(m+1,vector\u0026lt;int\u0026gt;(n+1)); if(m\u0026gt;n){ solve(T,S); return; } vector\u0026lt;int\u0026gt;dp(m+1); vector\u0026lt;int\u0026gt;dp2(m+1); for(int i=0;i\u0026lt;=m;i++)dp2[i]=0; for(int i=1;i\u0026lt;=n;i++){ dp[0]=i; char ti=T[i-1]; for(int j=1;j\u0026lt;=m;j++){ char sj=S[j-1]; int add=(ti==sj)?0:1; dp[j]=min({dp2[j]+1,dp[j-1]+1,dp2[j-1]+add}); } swap(dp,dp2); } if(dp2[m]\u0026lt;=k){ cout\u0026lt;\u0026lt;\u0026#34;Yes\u0026#34;; } else cout\u0026lt;\u0026lt;\u0026#34;No\u0026#34;; } int main(){ cin\u0026gt;\u0026gt;k\u0026gt;\u0026gt;S\u0026gt;\u0026gt;T; solve(S,T); return 0; } 现在发现不会超内存了，但是会超时，考虑优化动态规划的循环。\n优化循环： # 对于每一个字符的位置，我们只需要考虑在编辑距离范围内的子串部分，即 $\\max(1,i-K)$ 到 $\\min(m,i+K)$。\nAC 代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL k; string S,T; //vector\u0026lt;vector\u0026lt;int\u0026gt; \u0026gt;dp(500050,vector\u0026lt;int\u0026gt;(500050)); int dp[500050]; void solve(string S,string T){ LL n=T.size(),m=S.size(); if(abs(n-m)\u0026gt;k){ cout\u0026lt;\u0026lt;\u0026#34;No\u0026#34;;return; } //\tvector\u0026lt;vector\u0026lt;int\u0026gt; \u0026gt;dp(m+1,vector\u0026lt;int\u0026gt;(n+1)); if(m\u0026gt;n){ solve(T,S); return; } vector\u0026lt;int\u0026gt;dp(m+1); vector\u0026lt;int\u0026gt;dp2(m+1); for(int i=0;i\u0026lt;=min(m,k);i++)dp2[i]=i; for(LL i=1;i\u0026lt;=n;i++){ dp[0]=i; char ti=T[i-1]; for(LL j=max(1ll,i-k);j\u0026lt;=min(m,i+k);j++){ char sj=S[j-1]; int add=(ti==sj)?0:1; dp[j]=min({dp2[j]+1,dp[j-1]+1,dp2[j-1]+add}); } swap(dp,dp2); } if(dp2[m]\u0026lt;=k){ cout\u0026lt;\u0026lt;\u0026#34;Yes\u0026#34;; } else cout\u0026lt;\u0026lt;\u0026#34;No\u0026#34;; return; } int main(){ cin\u0026gt;\u0026gt;k\u0026gt;\u0026gt;S\u0026gt;\u0026gt;T; solve(S,T); return 0; } AC 记录。\n","date":"30 December 2024","externalUrl":null,"permalink":"/20241230210700.html/","section":"Posts","summary":"","title":"题解：AT_abc386_f [ABC386F] Operate K","type":"posts"},{"content":"","date":"23 December 2024","externalUrl":null,"permalink":"/categories/csp-j-%E5%85%A5%E9%97%A8%E7%BA%A7/","section":"Categories","summary":"","title":"CSP J 入门级","type":"categories"},{"content":"","date":"23 December 2024","externalUrl":null,"permalink":"/tags/%E9%A1%BA%E5%BA%8F%E7%BB%93%E6%9E%84/","section":"Tags","summary":"","title":"顺序结构","type":"tags"},{"content":" 题目 # 思路： # 语法题。\n考虑对于 $100\\%$ 的数据，$1\\le a,b,c \\le 10^9$，由此需要使用 long long。\n正方形的面积为边长乘边长。\n长方形的面积为长乘宽。\n在输入并计算好面积后判断哪个大就可以了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL a,b,c; int main(){ cin\u0026gt;\u0026gt;a\u0026gt;\u0026gt;b\u0026gt;\u0026gt;c; a*=a; b*=c; if(a\u0026gt;b)cout\u0026lt;\u0026lt;\u0026#34;Alice\u0026#34;; else cout\u0026lt;\u0026lt;\u0026#34;Bob\u0026#34;; return 0; } ","date":"23 December 2024","externalUrl":null,"permalink":"/20241223192400.html/","section":"Posts","summary":"","title":"题解：P5681 [CSP-J2019 江西] 面积","type":"posts"},{"content":"","date":"23 December 2024","externalUrl":null,"permalink":"/categories/csp-s-%E6%8F%90%E9%AB%98%E7%BA%A7/","section":"Categories","summary":"","title":"CSP S 提高级","type":"categories"},{"content":" 题目 # 思路： # 首先依据常识写出每月的天数。\n然后考虑怎么修改最小。\n先考虑 $M$ 的修改。\n为了使修改次数最小我们应尽可能的在 $M$ 这个月份不存在时在保证修改次数最小的情况下将他修改成每月天数尽可能多的月份。\n依据这个思路 $M$ 的修改可以分为两种情况。\n月份为 $0$，我们可以将 $M$ 修改为 $1$，因为 $1$ 月有 $31$ 天，且在月份为 $0$ 时只用修改一次。 月份大于 $12$ 时，又可以分为两种情况：当 $M$ 是十的倍数时，把月份修改成 $10$，因为 $10$ 月有 $31$ 天，且当 $M$ 是十的倍数时，月份日期的第二个字符一定是 $0$，由此只用修改一次；当 $M$ 不是十的倍数时，又可以分情况，当第二个字符也就是个位为 $1$ 或 $2$ 时，实际上是可以修改第一个字符为 $1$，但是要依据上面的思想“这个月份不存在时将他修改成每月天数尽可能多的月份”由此当月份为 $11$ 时修改成 $1$ 月更优，因为 $11$ 月有 $30$ 天而 $1$ 月有 $31$ 天并且都只要修改一次，对于个位不为 $1$ 或 $2$ 的其它情况，为了保证修改次数最小可以只保留个位，只需要修改一次就是把第一个字符修改成 $0$，例如月份为 $25$，修改后为 $05$，也就 $5$ 月。 接着考虑 $D$ 的修改。\n考虑到每个月份都至少有 $19$ 天，因此在这个日不存在时，可以把它的第一个字符修改成 $1$，例如 $56$ 修改后为 $16$。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int m,d; char ch; int myts[14]={0,31,28,31,30,31,30,31,31,30,31,30,31,0};// 每月天数 int main(){ cin\u0026gt;\u0026gt;m\u0026gt;\u0026gt;ch\u0026gt;\u0026gt;d; int ans=0; if(m==0)ans++,m=1; if(m\u0026gt;12){ ans++; if(m%10==0)m=10; else m=(m%10==2?10+m%10:m%10); } if(d\u0026gt;myts[m]||d==0){ ans++; d=d%10+10; } cout\u0026lt;\u0026lt;ans; return 0; } ","date":"23 December 2024","externalUrl":null,"permalink":"/20241223191500.html/","section":"Posts","summary":"","title":"题解：P5690 [CSP-S2019 江西] 日期","type":"posts"},{"content":" 思路： # 从一个点出发前往其它点，要求必须到达点的强度严格小于我们的强度的 $\\dfrac{1}{X}$ 倍，每次都可以从我们走过的地方形成的多边形的边上向没有到过的地方。\n考虑 bfs。\n与 bfs 模版不同的地方就在于每次都可以从我们走过的地方形成的多边形的边上向没有到过的地方，由此可以使用堆进行处理，每次把在边的旁边的点放进堆，每次取力量最小的点走。\n如果这时要走到点的强度已经不严格小于现在的强度的 $\\dfrac{1}{X}$ 倍了，那么可以直接输出现在的值，然后结束程序。\n可以这样的原因是我们采用了堆并且每次取的的最小强度，那么现在最小强度已经不严格小于现在的强度的 $\\dfrac{1}{X}$ 倍了，后面的强度只会大于或等于最小强度，所以也会是相同的结果，所以可以直接结束。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int H,W,p,q,x; LL s[510][510]; bool bj[510][510]; struct node{ LL x,y,v; friend bool operator \u0026lt;(node x,node y){ return x.v\u0026gt;y.v; } }; LL now; priority_queue\u0026lt;node\u0026gt;dl; int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; int main(){ cin\u0026gt;\u0026gt;H\u0026gt;\u0026gt;W\u0026gt;\u0026gt;x\u0026gt;\u0026gt;p\u0026gt;\u0026gt;q; for(int i=1;i\u0026lt;=H;i++){ for(int j=1;j\u0026lt;=W;j++){ cin\u0026gt;\u0026gt;s[i][j]; } } now=0; bj[p][q]=1; bool bjj=0; dl.push({p,q,s[p][q]}); while(dl.empty()==0){ node tamp=dl.top();dl.pop(); //\tcout\u0026lt;\u0026lt;tamp.x\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;tamp.y\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; if(bjj\u0026amp;\u0026amp;1.00000*now/x\u0026lt;=tamp.v)break; now+=tamp.v; bjj=1; for(int i=0;i\u0026lt;4;i++){ int tx=dx[i]+tamp.x,ty=dy[i]+tamp.y; if(tx\u0026gt;=1\u0026amp;\u0026amp;ty\u0026gt;=1\u0026amp;\u0026amp;tx\u0026lt;=H\u0026amp;\u0026amp;ty\u0026lt;=W\u0026amp;\u0026amp;!bj[tx][ty]){ bj[tx][ty]=1; dl.push({tx,ty,s[tx][ty]}); } } } cout\u0026lt;\u0026lt;now; return 0; } AC 记录。\n","date":"14 December 2024","externalUrl":null,"permalink":"/20241214221300.html/","section":"Posts","summary":"","title":"ABC384 E - Takahashi is Slime 2","type":"posts"},{"content":"","date":"14 December 2024","externalUrl":null,"permalink":"/tags/%E4%BC%98%E5%85%88%E9%98%9F%E5%88%97/","section":"Tags","summary":"","title":"优先队列","type":"tags"},{"content":" 题意： # 有多组数据。\n给出 $n$ 个字符串和 $k$ 个规则，求每个规则可以组成的所有密码。\n在规则中 # 表示的是一个给出的字符串，0 表示的是 $0$ 到 $9$ 中的一个数字。\n思路： # 深度优先搜索。\n深度优先搜索函数传入两个量 $now$ 和 $ans$。\n其中 $now$ 表示的是现在是规则里下标为 $now+1$ 字符，$ans$ 表示现在搜索到的答案。\n当规则里下标为 $now+1$ 字符为 # 时，遍历给出的字符串，对于每一个给出的字符串都放在 $ans$ 后进行一次 dfs，注意不能影响后面的操作，如当遍历的是第 $i$ 个给出的字符串时，不能影响第 $i+1$ 及其之后的操作。\n当规则里下标为 $now+1$ 字符为 0 时，把 $0$ 到 $9$ 按顺序放一次，同样不能影响后面的操作。\n具体的代码为：\nif(r[now]==\u0026#39;#\u0026#39;){// 是一个给出的字符串 for(int i=1;i\u0026lt;=n;i++){ dfs(now+1,ans+s[i]); } } else if(r[now]==\u0026#39;0\u0026#39;){// 是一个数字 for(int i=0;i\u0026lt;=9;i++){ char a=i+\u0026#39;0\u0026#39;; dfs(now+1,ans+a); } } 完整代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n,m; string r; string s[120]; void dfs(int now/*现在是 r 的第几个字符*/,string ans/*答案*/){ if(now\u0026gt;=r.size()){ cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; return; } if(r[now]==\u0026#39;#\u0026#39;){// 是一个给出的字符串 for(int i=1;i\u0026lt;=n;i++){ dfs(now+1,ans+s[i]); } } else if(r[now]==\u0026#39;0\u0026#39;){ for(int i=0;i\u0026lt;=9;i++){ char a=i+\u0026#39;0\u0026#39;; dfs(now+1,ans+a); } } } int main(){ ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); while(cin\u0026gt;\u0026gt;n){// 不确定组数，每次如果有 n 输入说明有一组 cout\u0026lt;\u0026lt;\u0026#34;--\\n\u0026#34;; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;s[i]; cin\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=m;i++){ cin\u0026gt;\u0026gt;r; dfs(0,\u0026#34;\u0026#34;); } } return 0; } ","date":"14 December 2024","externalUrl":null,"permalink":"/20241214114300.html/","section":"Posts","summary":"","title":"题解：UVA628 Passwords","type":"posts"},{"content":" 思路： # 一个数若要恰好有九个约数，它必须是以下两种形式之一：\n$p^8$，其中 $p$ 是一个质数。 $p^2 \\cdot q^2$，其中 $p$ 和 $q$ 是不同的质数。 为什么是这两种形式，原因如下。\n一个数的约数个数可以通过其质因数分解来确定。假设有一个正整数 $n$，它的质因数分解为：\n$$ n = p_1^{e_1} \\cdot p_2^{e_2} \\cdot \\ldots \\cdot p_k^{e_k} $$其中 $p_1, p_2, \\ldots, p_k$ 是不同的质数，而 $e_1, e_2, \\ldots, e_k$ 是这些质数在 $n$ 的质因数分解中的指数。\n根据约数个数定理，$n$ 的约数个数 $d(n)$ 可以通过下面的公式计算得出：\n$$ d(n) = (e_1 + 1)(e_2 + 1)\\dots(e_k + 1) $$要让 $n$ 恰好有九个约数，我们需要找到一种方法使得上述乘积等于 $9$。由于 $9$ 可以被分解为两个因数的乘积，即 $9 = 9 \\times 1 = 3 \\times 3$，因此我们可以得出两种可能的情况：\n当我们有一个质数的八次幂时，比如 $p^8$，这时只有一个质因子，其指数加一等于 $9$，所以它有 $8 + 1 = 9$ 个约数。 当我们有两个不同质数的平方相乘时，比如 $p^2 \\cdot q^2$，这时有两个质因子，每个的指数加一都等于 $3$，所以它们一起产生 $(2 + 1)(2 + 1) = 3 \\times 3 = 9$ 个约数。 这两种情况是能使一个数恰好有九个约数的形式，因为除此之外没有其他方法可以将 $9$ 分解为大于 $1$ 的整数之积。这就是为什么我们要找的数必须是 $p^8$ 或者 $p^2 \\cdot q^2$ 的形式。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long LL n; vector\u0026lt;bool\u0026gt;is_prime; vector\u0026lt;LL\u0026gt;primes; void hs1(LL lim){ is_prime.assign(lim+1,1); is_prime[0]=is_prime[1]=0; for(LL i=2;i*i\u0026lt;=lim;i++){ if(is_prime[i]){ for(LL j=i*i;j\u0026lt;=lim;j+=i)is_prime[j]=0; } } for(LL i=2;i\u0026lt;=lim;i++)if(is_prime[i])primes.push_back(i); } LL solve(LL n){ LL ans=0; int len=primes.size(); for(LL i=0;i\u0026lt;len;i++){ LL num=primes[i]*primes[i]*primes[i]*primes[i]*primes[i]*primes[i]*primes[i]*primes[i]; if(num\u0026gt;n)break; ans++; } for(LL i=0;i\u0026lt;len;i++){ LL a=primes[i]*primes[i]; if(a*a\u0026gt;n)break; for(LL j=i+1;j\u0026lt;len;j++){ LL num=a*primes[j]*primes[j]; if(num\u0026gt;n)break; ans++; } } return ans; } int main(){ cin\u0026gt;\u0026gt;n; hs1(static_cast\u0026lt;LL\u0026gt;(sqrt(n))); cout\u0026lt;\u0026lt;solve(n)\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; return 0; } ","date":"8 December 2024","externalUrl":null,"permalink":"/20241208095700.html/","section":"Posts","summary":"","title":"题解：AT_abc383_c [ABC383C] Humidifier 3","type":"posts"},{"content":"","date":"6 December 2024","externalUrl":null,"permalink":"/categories/codeforces/","section":"Categories","summary":"","title":"CodeForces","type":"categories"},{"content":" 思路： # 先考虑用队列和结构体进行模拟。\n把输入的那个数的 $id$ 和值一起放进队列，然后照着题目模拟就可以得出代码。\nstruct node{ int id; int num; }; queue\u0026lt;node\u0026gt;dl,d; int main(){ cin\u0026gt;\u0026gt;n; while(n--){ cin\u0026gt;\u0026gt;op; if(op==1){ int x;cin\u0026gt;\u0026gt;x; dl.push({++i,x}); } else if(op==2){ cout\u0026lt;\u0026lt;dl.front().id\u0026lt;\u0026lt;\u0026#34; \u0026#34;; dl.pop(); } else if(op==3){ queue\u0026lt;node\u0026gt;d; int id=0; int maxx=-5; int cnt=0; while(dl.empty()==0){ node tamp=dl.front(); dl.pop(); d.push(tamp); if(tamp.num\u0026gt;maxx){ maxx=tamp.num; id=tamp.id; } } while(d.empty()==0){ node tamp=d.front(); d.pop(); if(tamp.id!=id)dl.push(tamp); else cout\u0026lt;\u0026lt;tamp.id\u0026lt;\u0026lt;\u0026#34; \u0026#34;; } } } return 0; } 提交后发现超时了，考虑怎么优化。\n因为 $id$ 是唯一的，所以我们可以使用一个数组把输出过的 $id$ 进行标记，也就是这个 $id$ 表示的数被删除了，然后使用 priority_queue 处理操作 $3$，注意要输出最大的没有被删除的最早加入堆的数的 $id$。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n; int op; int i; bool bj[int(5*1e5+10)]; struct node{ int id; int num; friend bool operator\u0026lt;(const node \u0026amp;a,const node \u0026amp;b){ return a.num==b.num?a.id\u0026gt;b.id:a.num\u0026lt;b.num; } }; int last=1; priority_queue\u0026lt;node\u0026gt;dl; int main(){ ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin\u0026gt;\u0026gt;n; while(n--){ cin\u0026gt;\u0026gt;op; if(op==1){ int x;cin\u0026gt;\u0026gt;x; dl.push({++i,x}); } else if(op==2){ while(1){ if(!bj[last]){ cout\u0026lt;\u0026lt;last\u0026lt;\u0026lt;\u0026#34; \u0026#34;; bj[last]=1; break; } last++; } } else if(op==3){ while(bj[dl.top().id])dl.pop(); cout\u0026lt;\u0026lt;dl.top().id\u0026lt;\u0026lt;\u0026#34; \u0026#34;; bj[dl.top().id]=1; dl.pop(); } } return 0; } ","date":"6 December 2024","externalUrl":null,"permalink":"/20241206202800.html/","section":"Posts","summary":"","title":"题解：CF1468C Berpizza","type":"posts"},{"content":"","date":"18 November 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E5%AD%A6/","section":"Tags","summary":"","title":"数学","type":"tags"},{"content":" 思路： # 为了计算第 $i$ 个玩家的获胜概率，我们需要考虑所有可能的情况，即所有玩家轮流投掷直到某人获胜。给定 $n$ 个玩家，每个玩家投掷成功的概率为 $p$，失败的概率为 $1-p$。\n第 $i$ 个玩家的获胜方式可以分为以下几种情况：\n在第一轮中，第 $i$ 个玩家直接获胜，这发生在前 $i-1$ 个玩家都失败了之后，所以概率为 $(1-p)^{i-1} \\cdot p$。 第 $i$ 个玩家在第二轮获胜，这意味着所有 $n$ 个玩家在第一轮都失败了，然后前 $i-1$ 个玩家在第二轮也失败了，第 $i$ 个玩家在第二轮获胜，所以概率为 $(1-p)^{n} \\cdot (1-p)^{i-1} \\cdot p = (1-p)^{n+i-1} \\cdot p$。 同理，第i个玩家在第三轮获胜的概率为 $(1-p)^{2n+i-1} \\cdot p$。 这个模式可以无限延续下去，形成一个无穷等比数列，其中首项为 $(1-p)^{i-1} \\cdot p$，公比为 $(1-p)^{n}$。 因此，第i个玩家获胜的总概率P可以表示为这个无穷等比数列的和： $$P = (1-p)^{i-1} \\cdot p + (1-p)^{n+i-1} \\cdot p + (1-p)^{2n+i-1} \\cdot p + \\ldots $$这是一个等比数列的和，可以用公式计算： $$ P = \\frac{(1-p)^{i-1} \\cdot p}{1-(1-p)^{n}} $$ 代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int T=1; double solve(int n,int i,double p){ double q=1-p;// 失败概率 double fz=pow(q,i-1)*p;// 分子 double fm=1-pow(q,n);// 分母 double ans=fz/fm; return ans; } int main(){ cin\u0026gt;\u0026gt;T; while(T--){ int n,i; double p; cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;p\u0026gt;\u0026gt;i; printf(\u0026#34;%.4lf\\n\u0026#34;,p!=0?solve(n,i,p):0.0);// 三目运算符特判 } return 0; } AC 记录。\n","date":"18 November 2024","externalUrl":null,"permalink":"/20241118202800.html/","section":"Posts","summary":"","title":"题解：UVA10056 What is the Probability ?","type":"posts"},{"content":" 题目传送门。\n题意： # 完整题目：\nUVA10761 Broken Keyboard # 题目描述 # 某些键盘上的字母键坏了，但是所有非字母键都功能正常。尽管如此，我们还是需要输入一段文本，并希望知道可以完整输入多少行文本。此外，在我们仍然能够输出相同的文本行的基础上，我们还想知道哪些其他的字母键也可以坏掉。\n输入格式 # 程序应该从名为 keyboard.dat 的文件中读取输入。这个题目有但是不需要。 输入文件包含多个案例。每个案例的第一行列出了一系列坏掉的字母键。接下来的几行是希望输入的文本。此文本以以下行结束：\nEND 该行也应被处理。输入的任何一行都不会超过80个字符。当输入的第一个单词为 finish 时，表示输入终止，此案例不应被处理。\n输出格式 # 输出应格式化为一个表格，参见样例输出。表头占据4行，如下所示，另有一行用于标记字符位置；这行不是表头的一部分。\n12345678901234567890123456789012345678901234567890123456789 +----------+----------------+-----------------------------+ | Keyboard | # of printable | Additionally, the following | | | lines | letter keys can be broken | +----------+----------------+-----------------------------+ 在表格的剩余行中，对于每个输入案例，你应该输出两行，格式如下：\n表格的第一列报告了键盘的编号。键盘从 $1$ 开始顺序编号。这个数字应在第 $5$ 到 $7$ 个字符处右对齐打印。 表格的第二列报告了使用坏掉的键盘可以输入的文本行数。这个数字应在第 $19$ 到 $21$ 个字符处右对齐打印。每个案例中的文本行数不会超过 $999$ 行。 表格的第三列报告了哪些额外的字母键可以坏掉，而我们仍然能够输入第二列中报告的行数。额外的字母键应按字母顺序排列，从第 $31$ 个字符开始打印。 每个案例的第二行输出为一条水平线，如样例输出所示。 表格中使用的唯一空白字符是空格和换行符。 输入输出样例 # 输入 #1 # Xyz We will work with a basic time unit of an eighth-note. At any given time, your left foot and right foot will each be on distinct arrows. Only one foot may perform an action (changing arrows and/or tapping) during any time unit; jumping is not allowed. Also, you must remain facing forward in order to see the screen. This puts limitations on which feet you can use to hit which arrows. Finally, hitting two arrows in a row with the same foot (\u0026#34;double-tapping\u0026#34;) is exhausting, because you can’t shift your weight onto that foot. Ideally, you want to alternate feet all the way through a string of consecutive arrows. END life is sometimes tough END but one must not give up END finish 输出 #1 # +----------+----------------+-----------------------------+ | Keyboard | # of printable | Additionally, the following | | | lines | letter keys can be broken | +----------+----------------+-----------------------------+ | 1 | 4 | jkq | +----------+----------------+-----------------------------+ | 2 | 1 | abcdjkmnpqrsvwxyz | +----------+----------------+-----------------------------+ | 3 | 3 | acfhjklmpqrswxyz | +----------+----------------+-----------------------------+ 题目简述：\n给定一些不可以打出的字母，然后给出几个字符串，求有几个字符串能够打印和还有哪些字母不能打印时不会影响原来可以打印的字符串。\n思路： # 模拟，没啥思维。\n创建两个标记，一个标记不可以打出的字母，另一个标记需要使用的字符，为了方便，以下称为 $bj$ 和 $bj2$，我的代码里也是这两个名字。\n遍历需要输出的字符串，如果在这个字符串里有一个字符是在 $bj$ 里被标记过的，那么这个字符串不能输出。\n如果就算遍历完后，也没有一个字符是在 $bj$ 里被标记过的，那么就说明这个字符串是可以输出的，把这个字符串里的所有字符在 $bj2$ 里标记。\n没有在 $bj$ 和 $bj2$ 里出现过的字符就是还可以坏掉的字符按键。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long void ges(){ cout\u0026lt;\u0026lt;\u0026#34;+----------+----------------+-----------------------------+\\n\u0026#34;;// 这个是格式但是题目 pdf 文件有点问题，直接复制不一样 cout\u0026lt;\u0026lt;\u0026#34;| Keyboard | # of printable | Additionally, the following |\\n\u0026#34;;// 所以在 udebug 里看数据的正确输出就可以复制格式了 cout\u0026lt;\u0026lt;\u0026#34;| | lines | letter keys can be broken |\\n\u0026#34;;// 洛谷给出的样例也有点问题 cout\u0026lt;\u0026lt;\u0026#34;+----------+----------------+-----------------------------+\\n\u0026#34;;// 起码在我写的时候是这样的 return ; } string a; int main(){ //\tios::sync_with_stdio(0); //\tcin.tie(0),cout.tie(0); ges(); int count=0,case_num=0; while(getline(cin,a)){ if(a==\u0026#34;finish\u0026#34;)break; case_num++; unordered_set\u0026lt;char\u0026gt;bj;// 标记哪些字符坏了 for(int i=0;i\u0026lt;a.size();i++)bj.insert(tolower(a[i])); count=0; unordered_set\u0026lt;char\u0026gt;bj2;// 标记哪些字符需要使用 while(getline(cin,a)){ // 不在前面判断 END 是因为题目要求，END 也要处理 bool flag=0; for(int i=0;i\u0026lt;a.size();i++){ if(bj.count(tolower(a[i]))){ flag=1;break; } } if(!flag){ count++; for(int i=0;i\u0026lt;a.size();i++)bj2.insert(tolower(a[i])); } if(a==\u0026#34;END\u0026#34;)break;// 这组数据没了 } string ans; for(int i=0;i\u0026lt;26;i++){ char ch=i+\u0026#39;a\u0026#39;; if(!bj.count(ch)\u0026amp;\u0026amp;!bj2.count(ch))ans+=ch; } printf(\u0026#34;| %3d | %3d | %-27s |\\n\u0026#34;,case_num,count,ans.c_str()); cout\u0026lt;\u0026lt;\u0026#34;+----------+----------------+-----------------------------+\\n\u0026#34;; } return 0; } ","date":"16 November 2024","externalUrl":null,"permalink":"/20241116084100.html/","section":"Posts","summary":"","title":"题解：UVA10761 Broken Keyboard","type":"posts"},{"content":" 题目翻译： # 点击查看。\n思路： # 使用动态规划，计算在给定的字母表上，长度为 $n$ 的所有可能单词中，“紧致”单词的数量。\n然后，通过这个数量除以所有可能的单词数量来计算出“紧致”单词的百分百。\n对于 $dp_{i,j}$，其中 $i$ 表示长度为 $i$，$j$ 表示以 $j$ 结尾，当长度为 $1$ 时，每个数字 $j$ 都是一个“紧致”单词，因此我们可以初始化所有 $dp_{1,j}$ 为 $1$，其中 $0 \\le j \\le k$。\n对于 $i$ 不等于 $1$ 时的 $dp_{i,j}$ 等于什么呢？这需要分类讨论。\n对于长度为 $i$ 且以数字 $j$ 结尾的单词，它可以从以下几种情况转移而来：\n长度为 $i-1$ 且以数字 $j$ 结尾的单词。 长度为 $i-1$ 且以数字 $j-1$ 结尾的单词，注意 $j$ 需要大于 $0$。 长度为 $i-1$ 且以数字 $j+1$ 结尾的单词，注意 $j$ 需要小于 $k$。 因此，动态转移方程在 c++ 中可以表示为：\ndp[i][j]+=dp[i-1][j]; if(j!=0)dp[i][j]+=dp[i-1][j-1]; if(j!=k)dp[i][j]+=dp[i-1][j+1]; 代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; int n,k; double dp[110][15]; void solve(){ memset(dp,0,sizeof(dp)); for(int i=0;i\u0026lt;=k;i++)dp[1][i]=1; for(int i=2;i\u0026lt;=n;i++){ for(int j=0;j\u0026lt;=k;j++){ dp[i][j]+=dp[i-1][j]; if(j!=0)dp[i][j]+=dp[i-1][j-1]; if(j!=k)dp[i][j]+=dp[i-1][j+1]; } } double sum=0; double kn=pow(k+1,n); for(int i=0;i\u0026lt;=k;i++)sum+=dp[n][i]; //\tcout\u0026lt;\u0026lt;sum\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;kn\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; double ans=sum*100; ans/=kn; printf(\u0026#34;%.5lf\\n\u0026#34;,ans); //\tcout\u0026lt;\u0026lt;fixed\u0026lt;\u0026lt;setprecision(5)\u0026lt;\u0026lt;(sum*100.0)/kn\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; return; } int main(){ while(cin\u0026gt;\u0026gt;k\u0026gt;\u0026gt;n)solve(); return 0; } ","date":"3 November 2024","externalUrl":null,"permalink":"/20241103183300.html/","section":"Posts","summary":"","title":"题解：UVA10081 Tight Words","type":"posts"},{"content":"洛谷题目传送门。\nCF题目传送门。\n洛谷专栏传送门。\n题意： # 三兄弟约定见面，大哥编号为 $1$，中弟编号为 $2$，小弟编号为 $3$。\n到了开会时间，其中一个兄弟迟到了。\n给出准时到达的两个兄弟的编号，你需要确定迟到的那个兄弟的编号。\n思路： # 输入的是两个准时到达的，那么没有在输入里出现的就是迟到的。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int a[4]; int main(){ int x,y;cin\u0026gt;\u0026gt;x\u0026gt;\u0026gt;y; a[x]=a[y]=1; if(!a[1])cout\u0026lt;\u0026lt;1; else if(!a[2])cout\u0026lt;\u0026lt;2; else cout\u0026lt;\u0026lt;3; return 0; } AC 记录。\n","date":"7 October 2024","externalUrl":null,"permalink":"/20241007193000.html/","section":"Posts","summary":"","title":"题解：CF2010B Three Brothers","type":"posts"},{"content":"","date":"6 October 2024","externalUrl":null,"permalink":"/fcircle/","section":"xyx404","summary":"","title":"朋友圈","type":"page"},{"content":"如果想要查看友链，请点击此。博主的 Hugo 不提供友链展示以及友链申请，如有需要请前往 hexo 博客。\n免责声明 本博客遵守中华人民共和国相关法律。本页内容仅作为方便学习而产生的快速链接的链接方式，对与友情链接中存在的链接、好文推荐链接等均为其他网站。我本人能力有限无法逐个甄别每篇文章的每个字，并无法获知是否在收录后原作者是否对链接增加了违反法律甚至其他破坏用户计算机等行为。因为部分友链网站甚至没有做备案、域名并未做实名认证等，所以友链网站均可能存在风险，请你须知。\n所以在我力所能及的情况下，我会包括但不限于：\n针对收录的博客中的绝大多数内容通过标题来鉴别是否存在有风险的内容 在收录的友链好文推荐中检查是否存在风险内容 但是你在访问的时候，仍然无法避免，包括但不限于：\n作者更换了超链接的指向，替换成了其他内容 作者的服务器被恶意攻击、劫持、被注入恶意内容 作者的域名到期，被不法分子用作他用 作者修改了文章内容，增加钓鱼网站、广告等无效信息 不完善的隐私保护对用户的隐私造成了侵害、泄漏 最新文章部分为机器抓取，本站作者未经过任何审核和筛选，本着友链信任原则添加的。如果你发现其中包含违反中华人民共和国法律的内容，请及时联系和举报。该友链会被拉黑。\n如果因为从本页跳转给你造成了损失，深表歉意，并且建议用户如果发现存在问题在本页面进行回复。通常会很快处理。如果长时间无法得到处理，建议联系 xyx404@hotmail.com。\n被撤下友链的可能原因 超过 \\(365\\) 天未更新。 撤下本人友链。 违反了请勾选你符合的条件中的任意一条 出现问题的友链 None 我的友链信息 我的名称: xyx404 网站地址: https://xyx404.github.io/ 描述: 在努力和摆烂中选择了努力摆烂 AWA 头像: https://fastly.jsdelivr.net/npm/xyx404blogphoto@1.2.23/avatar.webp 网站截图: https://fastly.jsdelivr.net/npm/xyx404blogphoto@1.2.24/wzsyjt.png 友链申请格式：\n可选格式：card(如友情链接) 和 item(如小伙伴)。 - name: xyx404 # 友链名称 link: https://xyx404.github.io/ # 友链地址 avatar: https://fastly.jsdelivr.net/npm/xyx404blogphoto@1.2.23/avatar.webp # 显示头像 descr: 在努力和摆烂中选择了努力摆烂AWA # 友链简介 siteshot: https://fastly.jsdelivr.net/npm/xyx404blogphoto@1.2.24/wzsyjt.png # 申请card填，申请item留空(网站截图) ","date":"6 October 2024","externalUrl":null,"permalink":"/links/","section":"xyx404","summary":"","title":"友链申请","type":"page"},{"content":"洛谷专栏链接。\n题目： # 在我写题解时题目翻译有错误，故此处放上简略的正确翻译。\n有 $N$ 根竹子，编号从 $1$ 到 $N$。初始时，所有竹子的高度是 $0$，时间每过一秒，每根竹子的高度增加 $1$。\n有 $M$ 个砍伐计划，第 $i$ 个计划在 $T_i$ 时，将砍掉编号在 $L_i$ 和 $R_i$ 之间的竹子。当竹子砍下时高度为 $x$ 时，会得到 $x$ 的竹子。被砍掉的竹子高度将变为 $0$，之后还会生长。\n求可以得到多少竹子。\n完整题目，点击此处。\n思路： # 因为每过一秒高度都会增加一，且砍后会继续生长，所以我们可以只计算第 $i$ 个竹子最后是在什么时候被砍掉的，这个时间就是这个竹子的高度。\n实现，在输入后我们可以直接从最后开始向前遍历，因为题目满足输入的时间 $T_i$ 一定小于等于 $T_{i+1}$。\n定义一个 set，类型为 int，名字叫 $se$，用来标记这个竹子有没有砍过。\n用 lower_bound 函数查找在 $se$ 里第一个大于等于 $L_i$ 的值的位置，然后遍历编号在 $L_i$ 和 $R_i$ 之间的竹子，加上现在的时间也就是可以从竹子身上得到的竹子，并把这个竹子从 $se$ 里删除。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; set\u0026lt;int\u0026gt;se; int n,m; int t[200005],l[200005],r[200005]; long long ans=0; void solve(){ for(int i=1;i\u0026lt;=n;i++)se.insert(i);// 记录有没有砍 for(int i=m;i\u0026gt;=1;i--){ auto it=se.lower_bound(l[i]);// 二分查找第一个大于等于 l[i] 的值的位置 while(it!=se.end()\u0026amp;\u0026amp;*it\u0026lt;=r[i]){ ans+=t[i]; se.erase(it++); } } cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } int main(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=m;i++){ cin\u0026gt;\u0026gt;t[i]\u0026gt;\u0026gt;l[i]\u0026gt;\u0026gt;r[i]; } solve(); return 0; } ","date":"16 September 2024","externalUrl":null,"permalink":"/20240916193700.html/","section":"Posts","summary":"","title":"题解：AT_nikkei2019_final_d Deforestation","type":"posts"},{"content":"","date":"15 September 2024","externalUrl":null,"permalink":"/tags/%E6%9A%B4%E5%8A%9B/","section":"Tags","summary":"","title":"暴力","type":"tags"},{"content":" 思路： # 遍历每个点，查看点的右方和下方，如果两个点分属不同的州且颜色相同输出 No 并结束程序，否则继续遍历，为什么不用查看上方和下方呢，因为我们是按顺序遍历的从左到右，从上到下，在遍历的过程中已经确认这个点和这个点的上方和左方符合条件了。\n遍历完后输出 Yes。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int h,w,n,jz[210][210],c[500]; int dx[]={0,1},dy[]={1,0}; int main(){ memset(jz,0,sizeof(jz)); cin\u0026gt;\u0026gt;h\u0026gt;\u0026gt;w\u0026gt;\u0026gt;n; for(int i=1;i\u0026lt;=h;i++){ for(int j=1;j\u0026lt;=w;j++)cin\u0026gt;\u0026gt;jz[i][j]; } for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;c[i]; for(int i=1;i\u0026lt;=h;i++){ for(int j=1;j\u0026lt;=w;j++){ int tampx,tampy; tampx=dx[0]+i,tampy=dy[0]+j; if(jz[tampx][tampy]!=jz[i][j]){ if(c[jz[tampx][tampy]]==c[jz[i][j]]){ cout\u0026lt;\u0026lt;\u0026#34;No\\n\u0026#34;;return 0; } } tampx=dx[1]+i,tampy=dy[1]+j; if(jz[tampx][tampy]!=jz[i][j]){ if(c[jz[tampx][tampy]]==c[jz[i][j]]){ cout\u0026lt;\u0026lt;\u0026#34;No\\n\u0026#34;;return 0; } } } } cout\u0026lt;\u0026lt;\u0026#34;Yes\\n\u0026#34;; return 0; } ","date":"15 September 2024","externalUrl":null,"permalink":"/20240915210300.html/","section":"Posts","summary":"","title":"题解：AT_past202203_f 地図の塗り分け","type":"posts"},{"content":" 思路： # 暴力枚举直到现在的日期满足出现过的数字为 $2$ 种及以下。\n注意当月份和天数小于 $10$ 时，需要在前面补一个 $0$，而这个 $0$ 也是需要算进出现过的数字种数里的。\n统计出现过的数字种数可以使用 unordered_set，这是基于哈希表实现的无序关联容器，不会有重复的元素。\n至于每月天数可以做下洛谷的这题。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int y,m,d,tamp; char fuh; unordered_set\u0026lt;int\u0026gt;se; int day(int y,int m){ if(m==2){ if(y%4==0\u0026amp;\u0026amp;y%100!=0)return 29; else if(y%400==0)return 29; else{ return 28; } } if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)return 31; else if(m==4||m==6||m==9||m==11)return 30; } bool check(){ unordered_set\u0026lt;int\u0026gt;s; int tamp=y; if(m\u0026lt;10||d\u0026lt;10)s.insert(0); while(tamp){ s.insert(tamp%10); tamp/=10; } tamp=m; while(tamp){ s.insert(tamp%10); tamp/=10; } tamp=d; while(tamp){ s.insert(tamp%10); tamp/=10; } if(s.size()\u0026lt;=2){ return 1; } return 0; } int main(){ cin\u0026gt;\u0026gt;y\u0026gt;\u0026gt;fuh\u0026gt;\u0026gt;m\u0026gt;\u0026gt;fuh\u0026gt;\u0026gt;d; tamp=y; if(m\u0026lt;10||d\u0026lt;10)se.insert(0); while(tamp){ se.insert(tamp%10); tamp/=10; } tamp=m; while(tamp){ se.insert(tamp%10); tamp/=10; } tamp=d; while(tamp){ se.insert(tamp%10); tamp/=10; } if(se.size()\u0026lt;=2){ printf(\u0026#34;%d/%02d/%02d\u0026#34;,y,m,d); //\tcout\u0026lt;\u0026lt;y\u0026lt;\u0026lt;\u0026#34;/\u0026#34;\u0026lt;\u0026lt;m\u0026lt;\u0026lt;\u0026#34;/\u0026#34;\u0026lt;\u0026lt;d; } else{ while(1){ if(d-1==day(y,m)){ d=1;m++; } if(m==13){ y++;m=1; } if(check()){ printf(\u0026#34;%d/%02d/%02d\u0026#34;,y,m,d);//cout\u0026lt;\u0026lt;y\u0026lt;\u0026lt;\u0026#34;/\u0026#34;\u0026lt;\u0026lt;m\u0026lt;\u0026lt;\u0026#34;/\u0026#34;\u0026lt;\u0026lt;d; break; } d++; } } cout\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; return 0; } ","date":"15 September 2024","externalUrl":null,"permalink":"/20240915200900.html/","section":"Posts","summary":"","title":"题解：AT_past202203_e 良い日付","type":"posts"},{"content":"","date":"24 August 2024","externalUrl":null,"permalink":"/categories/%E8%93%9D%E6%A1%A5%E6%9D%AF%E7%9C%81%E8%B5%9B/","section":"Categories","summary":"","title":"蓝桥杯省赛","type":"categories"},{"content":" 思路： # 因为可以添加 l、q、b 这三个字符，所以我们可以先把左、右端的这三个字符先拿出来，然后判断一下中间是否回文，如果不回文输出 No，如果回文再判断一下左，右端拿出来的字符串。\n因为只能在开头处加字符，所以当从右端取出来的字符串的字符数量比左端取出来的字符串的字符数量少时，输出 No，当从右端取出来的字符串的字符数量比左端取出来的字符串的字符数量多时，分别从两个字符串的结尾对比字符，如果字符不一样则输出 No，因为只能加，不能删。\n如果以上都过了，那么就输出 Yes。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long string s; int T; int main(){ cin\u0026gt;\u0026gt;T; while(T--){ cin\u0026gt;\u0026gt;s; int flag1=0,flag2=0; int len=s.size();s=\u0026#34; \u0026#34;+s; string c=\u0026#34;\u0026#34;; for(int i=1;i\u0026lt;=len;i++){// 从开头开始 if(!flag1){ if(s[i]==\u0026#39;l\u0026#39;||s[i]==\u0026#39;q\u0026#39;||s[i]==\u0026#39;b\u0026#39;)c+=s[i];// 取出字符串 else{ flag1=i;break;// 标记从哪里开始不同的 } } } string d=\u0026#34;\u0026#34;; for(int i=len;i\u0026gt;=1;i--){// 从结尾开始 if(!flag2){ if(s[i]==\u0026#39;l\u0026#39;||s[i]==\u0026#39;q\u0026#39;||s[i]==\u0026#39;b\u0026#39;)d+=s[i]; else{ flag2=i;break; } } } bool bj=0; for(int i=d.size()-1,j=c.size()-1;i\u0026gt;=0\u0026amp;\u0026amp;j\u0026gt;=0;i--,j--){ if(d[i]!=c[j]){ bj=1;break; } } if(bj||d.size()\u0026lt;c.size()){ cout\u0026lt;\u0026lt;\u0026#34;No\\n\u0026#34;;continue; } c=\u0026#34;\u0026#34;,d=\u0026#34;\u0026#34;; for(int i=flag1,j=flag2;i\u0026lt;=j;i++,j--){// 判断中间的字符串是否回文 c+=s[i];d+=s[j]; } if(c==d)cout\u0026lt;\u0026lt;\u0026#34;Yes\\n\u0026#34;; else cout\u0026lt;\u0026lt;\u0026#34;No\\n\u0026#34;; } return 0; } ","date":"24 August 2024","externalUrl":null,"permalink":"/20240824103500.html/","section":"Posts","summary":"","title":"题解：P10905 [蓝桥杯 2024 省 C] 回文字符串","type":"posts"},{"content":" 思路： # 定义两个数组 $a$ 和 $b$，其中 $a$ 存输入为正数的情况，$b$ 存输入为负数时负数的绝对值，在定义两个变量 $cnt$ 存起点矿石的情况，$ans$ 存最多挖矿石几个矿石不包含起点矿石的情况。\n输入完后将 $a$ 数组与 $b$ 数组排序。\n排序过后枚举情况，共有两种情况，第一种完全不向右侧挖掘，第二种向右走两次向左走一次，每次枚举完情况后更新 $ans$ 的值。\n最终的结果为在输入中 $ans+cnt$。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long vector\u0026lt;LL\u0026gt;a,b;// 分别存储正数和负数(取绝对值后) int main(){ LL n,m; cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; LL cnt=0;// 计算坐标为 0 的矿洞数量 a.push_back(0);// 下标从 1 开始 b.push_back(0); for(LL i=0;i\u0026lt;n;i++){ LL sr; cin\u0026gt;\u0026gt;sr; if(sr==0)cnt++;// 坐标为 0 的矿洞直接计算 else if(sr\u0026gt;0)a.push_back(sr);// 正数放入 a 数组 else b.push_back(-sr);// 负数取绝对值后放入 b 数组 } sort(a.begin()+1,a.end()); sort(b.begin()+1,b.end()); LL ans=0,pos1=b.size()-1,pos2=b.size()-1; for(LL i=0;i\u0026lt;=a.size()-1;i++){// 枚举情况 LL x=m-a[i]; if(x\u0026lt;0)continue; while(b[pos1]\u0026gt;x/2)pos1--; ans=max(ans,i+pos1); //\tcout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34; 1 \\n\u0026#34;; // 完全不向右侧挖掘 x=m-a[i]*2; if(x\u0026lt;0)continue; while(b[pos2]\u0026gt;x)pos2--; ans=max(ans,i+pos2); //\tcout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34; 2 \\n\u0026#34;; // 向右走两次向左走一次 } //\tcout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;cnt\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; cout\u0026lt;\u0026lt;ans+cnt; return 0; } ","date":"24 August 2024","externalUrl":null,"permalink":"/20240824100500.html/","section":"Posts","summary":"","title":"题解：P10904 [蓝桥杯 2024 省 C] 挖矿","type":"posts"},{"content":"","date":"4 August 2024","externalUrl":null,"permalink":"/tags/%E9%93%BE%E8%A1%A8/","section":"Tags","summary":"","title":"链表","type":"tags"},{"content":" 思路： # 定义结构体，结构体包含三个成员 $left$ 表示左边的人，$right$ 表示右边的人，$cz$ 表示这两个人的差值，自定义排序：\nbool operator \u0026lt;(const node \u0026amp;X)const{ return cz==X.cz?left\u0026gt;X.left:cz\u0026gt;X.cz; } 定义优先队列，类型为上面定义的结构体。\n定义标记数组 $bj$ 标记这个人是否已经被搭配了。\n定义二维数组 $ans$ 存答案。\n定义 $ans2$ 存答案的数量。\n定义数组 $last$ 和 $nex$ 模拟链表，记录上一个人和下一个人。\n在输入时顺便把 $last$ 和 $nex$ 初始化，初始化时 $last_i$ 的值为 $i-1$，$nex_i$ 的值为 $i+1$。\n在输入完后，遍历字符串，直到现在遍历到的是字符串的最后一个字母，注意是遍历到而不是遍历完，当字符串的第 $i$ 个字符与第 $i+1$ 个字符不同时，也就是他们为异性时，把他们及他们的差值的绝对值存进结构体加入优先队列。\n然后进入循环，循环条件为队列不为空，在循环内，先把队列里的结构体取出来，然后弹出，然后判断下，取出来的结构体里的 $left$ 和 $right$ 是否在 $bj$ 里被标记过，如果其中有一个被标记过那么直接开始下一次循环，否则标记他们，并把他们存在 $ans$ 数组里，并让 $ans2$ 加一，在这之后维护链表，如果去掉他们两个后，$left$ 原本的左边的人与 $right$ 原本右边的人为异性那么把他们与他们的差值的绝对值存进结构体加入优先队列。\n最后先输出 $ans2$，然后把 $ans$ 数组里被存过的答案输出就行了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int last[200005],nex[200005],a[200005],ans[200005][3],ans2; struct node{ itn left,right,cz; bool operator \u0026lt;(const node \u0026amp;X)const{ return cz==X.cz?left\u0026gt;X.left:cz\u0026gt;X.cz; } }; priority_queue\u0026lt;node\u0026gt;dl; bool bj[200005]; string str; itn n; int main(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;str; str=\u0026#34; \u0026#34;+str; for(int i=1;i\u0026lt;=n;i++){ cin\u0026gt;\u0026gt;a[i]; last[i]=i-1; nex[i]=i+1; } for(int i=1;i\u0026lt;n;i++){ if(str[i]!=str[i+1]){ dl.push(node{i,i+1,abs(a[i]-a[i+1])}); } } while(!dl.empty()){ node tamp=dl.top();dl.pop(); if(bj[tamp.left]||bj[tamp.right])continue; ans[++ans2][1]=tamp.left; ans[ans2][2]=tamp.right; bj[tamp.left]=bj[tamp.right]=1; int l=last[tamp.left],ne=nex[tamp.right]; nex[l]=ne;last[ne]=l; if(l\u0026lt;1||ne\u0026gt;n)continue; if(str[l]!=str[ne]){ dl.push(node{l,ne,abs(a[l]-a[ne])}); } } cout\u0026lt;\u0026lt;ans2\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; for(itn i=1;i\u0026lt;=ans2;i++){ cout\u0026lt;\u0026lt;ans[i][1]\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;ans[i][2]\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } return 0; } ","date":"4 August 2024","externalUrl":null,"permalink":"/20240804203000.html/","section":"Posts","summary":"","title":"题解：CF45C Dancing Lessons","type":"posts"},{"content":"","date":"4 August 2024","externalUrl":null,"permalink":"/tags/%E7%BB%93%E6%9E%84%E4%BD%93%E6%8E%92%E5%BA%8F/","section":"Tags","summary":"","title":"结构体排序","type":"tags"},{"content":" 思路： # 定义一个 unordered_set 用于标记出现过那些人，定义名字为 $se$。\n定义一个 unordered_map 记录每个人所获得的分数，键为字符串类型，值为整形，定义名字为 $person$。\n定义结构体用于存每个人的分数，此结构体排序用。\n观察题目所给出的操作的输入格式，发现操作的标志一定是在这一行的第二个字符串，而这一行字符串的数量一定大于三，所以我们可以先输入前三个字符串，然后判断第二个字符串是什么操作。\n如果第二个字符串等于 likes 那么第三个字符串就是第二个人的名字。\n如果第二个字符串为 posted 或 commented 时，那么第二个人名的位置就在第四个字符串，我们把他输入进来。\n在输入找到第二个人的名字后，我们用 erase 把第二个人名后面多余的 's 删除，使这个字符串只有人名，然后判断第一个人名和第二个人名中有没有淘淘，因为只有和淘淘互动了，他跟淘淘的因子分数才会增加，然后再判断下这些人名有没有在 se 里面出现过，如果没有出现过就把他加入到里面，最后把这些操作里的最后一个字符串输入进来就行了。\n我们用创建一个 $it$ 用于历遍 $se$，使他等于 se.begin()，之后我们进入循环每次让 it++ 循环条件为 it!=se.end()，在循环过程中我们把每个人的名字以及分数存进结构体。\n在循环结束后，我们用 sort 进行排序，排序函数为当分数一样时按名字的字典序排序，否则按分数的大小排序。\n排序完后，我们输出，当现在输出的这个人的名字不是淘淘的名字时我们就输出。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n; unordered_set\u0026lt;string\u0026gt;se; unordered_map\u0026lt;string,int\u0026gt;person; struct node{ string name; int soccer; }ren[110]; bool cmp(node x,node y){ if(x.soccer==y.soccer){// 当分数一样时按名字的字典序排序 return x.name\u0026lt;y.name; } return x.soccer\u0026gt;y.soccer;// 按分数的大小排序 } int main(){ string x;cin\u0026gt;\u0026gt;x; int n;cin\u0026gt;\u0026gt;n; while(n--){ string a,b,c; cin\u0026gt;\u0026gt;a\u0026gt;\u0026gt;b\u0026gt;\u0026gt;c;// 先输入前三个字符串 if(b==\u0026#34;likes\u0026#34;){ c.erase(c.size()-2);string Y=c;// 用 erase 删掉 \u0026#39;s if(a==x||Y==x){ if(a!=x){ person[a]+=5; } if(Y!=x){ person[Y]+=5; } } if(!se.count(a))se.insert(a);if(!se.count(Y))se.insert(Y); cin\u0026gt;\u0026gt;c; } else if(b==\u0026#34;commented\u0026#34;){ cin\u0026gt;\u0026gt;c;c.erase(c.size()-2);string Y=c;// 用 erase 删掉 \u0026#39;s if(a==x||Y==x){ if(a!=x){ person[a]+=10; } if(Y!=x){ person[Y]+=10; } } if(!se.count(a))se.insert(a);if(!se.count(Y))se.insert(Y); cin\u0026gt;\u0026gt;c; } else if(b==\u0026#34;posted\u0026#34;){ cin\u0026gt;\u0026gt;c;c.erase(c.size()-2);string Y=c;// 用 erase 删掉 \u0026#39;s if(a==x||Y==x){ if(a!=x){ person[a]+=15; } if(Y!=x){ person[Y]+=15; } } if(!se.count(a))se.insert(a);if(!se.count(Y))se.insert(Y); cin\u0026gt;\u0026gt;c; } } auto it=se.begin(); for(int i=1;it!=se.end();it++,i++){// 存进结构体 ren[i].name=*it; ren[i].soccer=person[*it]; } sort(ren+1,ren+1+se.size(),cmp);// 排序 for(int i=1;i\u0026lt;=se.size();i++){ if(ren[i].name!=x)cout\u0026lt;\u0026lt;ren[i].name\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;// 输出 } return 0; } ","date":"4 August 2024","externalUrl":null,"permalink":"/20240804192000.html/","section":"Posts","summary":"","title":"题解：CF75B Facetook Priority Wall","type":"posts"},{"content":" 思路： # 定义三个 unordered_map，两个 unordered_map 的键和值都是 int 类型，分别存被访问的次数定义名字为 $mp$ 和是在什么时候存进来的定义名字为 $jl$，第三个 unordered_map 的键为 int 类型，而值为 vector 数组，用来存访问次数为键的内存页是几定义名字为 $se$。\n定义 $c$ 为需要访问的虚拟内存页的编号，再定义一个 $minn$ 表示现在最少的访问次数。\n如果要访问的内存页存在也就是 mp.count(c) 为一，那么让我们的答案加一，然后把 $se$ 里的数组里的 $c$ 删除，接着把 $c$ 加入到 $se$ 的键为这次操作后 $c$ 被访问的次数的数组里，然后判断一下在这次操作后原本 $se$ 的键为 $minn$ 的数组大小是否为零，如果为零则代表 $c$ 原本是这个数组里唯一的元素，而 $c$ 的访问次数增加后，这个数组里就没有元素了，也就是最少的访问次数现在不是 $minn$ 了而是 $minn$ 加上一，我们让 $minn$ 加一。\n如果要访问的内存页不存在，则先判断一下现在 $mp$ 里键的数量是否小于 $n$：\n如果小于 $n$，则把 $mp_c$ 赋值为一，然后将 $jl_c$ 赋值为现在是第几个操作，然后在 $se$ 里键为一的数组里加入 $c$，最后把 $minn$ 赋值为一，因为 $c$ 是刚刚加入的，所以只被访问了一次，是最少的访问次数。\n如果不小于 $n$ 则把 $se$ 里键为 $minn$ 的数组里的第一个值删除并把 $c$ 加入 $se$ 里键为一的数组里。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n,m,ans=0; unordered_map\u0026lt;int,int\u0026gt;mp;// 被访问的次数 unordered_map\u0026lt;int,int\u0026gt;jl;// 是在什么时候存进来的 unordered_map\u0026lt;int,vector\u0026lt;int\u0026gt; \u0026gt;se;// 访问次数，是什么 int minn=1e9;// 最少的访问次数 int main(){ ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; jl[int(1e9+1)]=m+1; int c; for(int i=1;i\u0026lt;=m;i++){ cin\u0026gt;\u0026gt;c; if(mp.count(c)){ ans++; auto it=find(se[mp[c]].begin(),se[mp[c]].end(),c); se[mp[c]].erase(it); if(se[minn].size()==0)minn++; mp[c]++; se[mp[c]].push_back(c); } else{ if(mp.size()\u0026lt;n){ mp[c]=1; jl[c]=i; se[mp[c]].push_back(c); minn=1; } else{ int sanc=1e9+1; auto it=se.begin(); vector\u0026lt;int\u0026gt;sz(se[minn]); sanc=sz[0]; auto itt=find(se[mp[sanc]].begin(),se[mp[sanc]].end(),sanc); se[mp[sanc]].erase(itt); if(se[mp[sanc]].size()==0)se.erase(mp[sanc]); mp.erase(sanc); jl.erase(sanc); mp[c]=1; jl[c]=i; minn=1; se[mp[c]].push_back(c); } } } cout\u0026lt;\u0026lt;ans; return 0; } ","date":"2 August 2024","externalUrl":null,"permalink":"/20240802163700.html/","section":"Posts","summary":"","title":"题解：P2318 [HNOI2005] 虚拟内存","type":"posts"},{"content":" 思路： # 看题，需要在矩阵上找出一条起始点任意的路径（可以重复经过某个格子），使得字典序最大。\n那么通过可以重复这四个字我们可以知道最后求出的字符串的长度一定是小于等于二的。\n所以，我们可以在输入时查找最大的字母（为了保证字典序最大）用一个变量 $c$ 存下来，输入完之后，历遍矩阵，当历遍到的字母为我们找到的最大的字母时，查找他上下左右四个方向的最大的字母，然后用 $ch$ 存下来。\n最后在循环结束后在 $c$ 与 $ch$ 不相同时输出 $ch$ 就可以了，相同的情况就不要输出了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long int n,m; char ch; char jz[1500][1500]; int dx[]={1,-1,0,0},dy[]={0,0,-1,1}; int main(){ int mx=0,my=0; char c=\u0026#39;A\u0026#39;-1; cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=n;i++){ for(int j=1;j\u0026lt;=m;j++){ cin\u0026gt;\u0026gt;jz[i][j];if(jz[i][j]!=\u0026#39;#\u0026#39;)if(jz[i][j]\u0026gt;c)c=jz[i][j];// 输入并查找最大的字母 } } cout\u0026lt;\u0026lt;c;// 为了保证字典序最大第一个字母一定是最大的可以直接输出 for(int i=1;i\u0026lt;=n;i++){ for(int j=1;j\u0026lt;=m;j++){ if(jz[i][j]!=\u0026#39;#\u0026#39;)if(jz[i][j]==c){ mx=i;my=j; for(int i=0;i\u0026lt;4;i++){ int tx=mx+dx[i],ty=dy[i]+my; if(tx\u0026lt;1||ty\u0026lt;1||tx\u0026gt;n||ty\u0026gt;m)continue; if(jz[tx][ty]==\u0026#39;#\u0026#39;)continue; if(ch\u0026lt;jz[tx][ty])ch=jz[tx][ty]; } } } } if(ch!=c)cout\u0026lt;\u0026lt;ch;// 在不相等的时候才输出不然会输出两个相同的字母，如果不这样写就不满足题目中如果答案是无限的，只需输出它的最短循环节 return 0; } ","date":"1 July 2024","externalUrl":null,"permalink":"/20240701112600.html/","section":"Posts","summary":"","title":"题解：P10677 『STA - R6』inkar-usi","type":"posts"},{"content":" 思路： # 使用 dfs 算法，创建一个数组存当前已经找到的数字。\n深度优先搜索代码实现：传入的量为现在找的是第几个数，第一次传入的是一，表示现在找的是第一个数，当传入的数减一等于 $m$ 时输出。\n输出函数实现：循环定义的 $i$ 表示当前已经找到的数字的数组的下标，如果你下标是从零开始存的就从零历遍到 $m-1$，如果是从一开始存的就从 $1$ 历遍到 $m$，注意不要忘了最后换行。\n因为题目中要求字典序较小的排在前面，所以我们就不定义标记数组了，下一次搜索就直接从上一个数加一开始。\n完整代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define LL long long #define itn int #define ull unsigned long long ull n,m; ull sc[30];// 存现在找到的数字 void print(){// 输出 for(int i=1;i\u0026lt;=m;i++)cout\u0026lt;\u0026lt;sc[i]\u0026lt;\u0026lt;\u0026#34; \u0026#34;; cout\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } void dfs(int x){// 搜索 if(x-1==m){ print();return ; } for(int i=sc[x-1]+1;i\u0026lt;=n;i++){ sc[x]=i; dfs(x+1); } } int main(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m;// 输入 dfs(1); return 0; } ","date":"15 May 2024","externalUrl":null,"permalink":"/20240515192000.html/","section":"Posts","summary":"","title":"题解：P10448 组合型枚举","type":"posts"},{"content":"","date":"3 May 2024","externalUrl":null,"permalink":"/tags/%E6%9E%9A%E4%B8%BE/","section":"Tags","summary":"","title":"枚举","type":"tags"},{"content":"","date":"3 May 2024","externalUrl":null,"permalink":"/tags/%E5%89%8D%E7%BC%80%E5%92%8C/","section":"Tags","summary":"","title":"前缀和","type":"tags"},{"content":" 思路： # 使用前缀和，考虑到输入的询问范围可能会有 $l$ 相同，所以先用一个结构体把输入的 $l$ 和 $r$ 和输入的顺序存起来，然后进行排序哪个左端点更小，如果同时左端点相同，那么比较哪个右端点更小。\n在 $l$ 相等时，我们可以直接按照上一个的答案往下搜这样重复的部分就不用重复计算历遍了。\n为什么可以直接按照上一个的答案往下搜，因为我们在一开始排过序，只要这个结构体排完序后在前一个结构体后面，并且 $l$ 相同，那么后面的结构体的 $r$ 一定大于等于前面的结构体的 $r$，所以可以直接按照上一个的答案往下搜。\n当 $l$ 不相等时，就正常地直接历遍，然后存答案就可以了。\n完整代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define ull unsigned long long #define LL long long ull tamp; const int MAXN=10001; const int MAXQ=1000001; ull n,q; ull sum[MAXN]; ull a[100010]; struct node{ ull l,r,id,ans;// 存左、右端点和输入顺序和答案 }wt[MAXQ]; bool cmp_id(node x,node y){// 通过输入顺序还原后再输出 return x.id\u0026lt;y.id; } bool cmp_lr(node x,node y){// 排序哪个先开始如果同时开始那么比较哪个先结束 if(x.l==y.l)return x.r\u0026lt;y.r;// 如果左端点也就是 l 相同那么比较右端点 return x.l\u0026lt;y.l;// 如果 l 不相同比较那个的 l 更前面 } void find(){ ull su; for(ull i=1;i\u0026lt;=q;i++){//q 个问题 if(wt[i].l!=wt[i-1].l){// 上一个结构体的 l 和这个结构体的 l 不相同 su=a[wt[i].l];//su 赋值为 a[wt[i].l] for(int j=wt[i].l+1;j\u0026lt;=wt[i].r;j++){// 从 wt[i].l+1 历遍到 wt[i].r 的答案 su^=(sum[j]-sum[wt[i].l-1]); } wt[i].ans=su;// 答案赋值 } else{ if(wt[i].l==wt[i-1].l\u0026amp;\u0026amp;wt[i].r==wt[i-1].r){// 如果和上一个结构体的 l 和 r 相同说明答案也相同 wt[i].ans=su;// 直接赋值 } else{ for(ull j=wt[i-1].r+1;j\u0026lt;=wt[i].r;j++){// 因为 l 相同且后面的结构体的 r 一定大于等于前面的结构体的 r 所以直接从前面的结构体的 r 的后面开始历遍 su^=(sum[j]-sum[wt[i].l-1]); } wt[i].ans=su;// 答案赋值 } } } } int main(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;q; for(ull i=1;i\u0026lt;=n;i++){ cin\u0026gt;\u0026gt;a[i]; sum[i]=sum[i-1]+a[i];// 计算前缀和 } for(ull i=1;i\u0026lt;=q;i++)cin\u0026gt;\u0026gt;wt[i].l\u0026gt;\u0026gt;wt[i].r,wt[i].id=i;// 输入问题的 l 和 r 并标记输入顺序 sort(wt+1,wt+1+q,cmp_lr);// 按 l 和 r 排序 find(); sort(wt+1,wt+1+q,cmp_id);// 按输入顺序排序 for(ull i=1;i\u0026lt;=q;i++)cout\u0026lt;\u0026lt;wt[i].ans\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;// 输出答案 return 0; } ","date":"3 May 2024","externalUrl":null,"permalink":"/20240503134900.html/","section":"Posts","summary":"","title":"题解：P10401 「XSOI-R1」区间操作","type":"posts"},{"content":"","date":"28 April 2024","externalUrl":null,"permalink":"/categories/gesp/","section":"Categories","summary":"","title":"GESP","type":"categories"},{"content":" 思路： # 因为只有通过删除一个字符，或插入一个字符，或修改一个字符变成另一个字符串才是相似的，所以我们可以分成五种情况。\n第一种情况，如果第一个字符串和第二个字符串是相同的，那么这两个字符串是相似的，代码如下。\nif(a==b){// 特判如果 a 与 b 相同的情况 cout\u0026lt;\u0026lt;\u0026#34;similar\\n\u0026#34;;continue; } 第二种情况，如果第一个字符串的长度等于第二个字符串的长度且两个字符串不相同，如果它们是相似的，那么只能是修改一个字符出来的，两个字符串之间至少有第一个字符串的长度减一个字符是相同的，且位置相同，代码如下。\nif(a.size()==b.size()){// a 的长度与 b 的长度相同 for(int i=0;i\u0026lt;a.size();i++){ if(a[i]==b[i])cnt++;// 都用 i 因为要在同一个位置 } if(cnt+1==a.size()||cnt==a.size()){// 至少有 a.size()-1 个字符相同且在同一个位置 cout\u0026lt;\u0026lt;\u0026#34;similar\\n\u0026#34;; } else cout\u0026lt;\u0026lt;\u0026#34;not similar\\n\u0026#34;; } 第三种情况第一个字符串的长度比第二个字符串的长度小一，如果它们是相似的，那么只能是通过插入一个字符出来的，这下不用是同一个位置了，但是顺序要一样，所以我们可以定义一个变量 $cc$ 用来储存现在历遍到了第一个字符串的第几个字符，第二个字符串用循环历遍，代码如下。\nelse if(a.size()+1==b.size()){//a 的长度加一等于 b 的长度 for(int i=0;i\u0026lt;b.size();i++){ if(a[cc]==b[i])cnt++,cc++; } if(cnt==a.size()){// 因为是插入的所以相同的要有 a 的长度个，也就是去掉那个插入的字符后要完全相同 cout\u0026lt;\u0026lt;\u0026#34;similar\\n\u0026#34;; } else cout\u0026lt;\u0026lt;\u0026#34;not similar\\n\u0026#34;; } 第四种情况第一个字符串的长度比第二个字符串的长度大一，如果它们是相似的，那么只能是通过删除一个字符出来的，因为它们是相似的所以可以看做第一个字符串是第二个字符串通过插入操作得到的，只需要交换两个字符串然后和第三种情况一样写就可以了，代码如下。\nelse if(a.size()-1==b.size()){// a 的长度减一等于 b 的长度 swap(a,b);// 交换 a 和 b for(int i=0;i\u0026lt;b.size();i++){ if(a[cc]==b[i])cnt++,cc++; } if(cnt==a.size()){ cout\u0026lt;\u0026lt;\u0026#34;similar\\n\u0026#34;; } else cout\u0026lt;\u0026lt;\u0026#34;not similar\\n\u0026#34;; } 第五种情况，以上情况都不是也就是说肯定不是通过删除一个字符，或插入一个字符，或修改一个字符出现的，所以可以直接输出，代码如下。\nelse{ cout\u0026lt;\u0026lt;\u0026#34;not similar\\n\u0026#34;; } 完整代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; int main(){ int t; cin\u0026gt;\u0026gt;t; while(t--){ string a,b; cin\u0026gt;\u0026gt;a\u0026gt;\u0026gt;b; int cnt=0,cc=0; if(a==b){// 1 cout\u0026lt;\u0026lt;\u0026#34;similar\\n\u0026#34;;continue; } if(a.size()==b.size()){// 2 for(int i=0;i\u0026lt;a.size();i++){ if(a[i]==b[i])cnt++; } if(cnt+1==a.size()||cnt==a.size()){ cout\u0026lt;\u0026lt;\u0026#34;similar\\n\u0026#34;; } else cout\u0026lt;\u0026lt;\u0026#34;not similar\\n\u0026#34;; } else if(a.size()+1==b.size()){// 3 for(int i=0;i\u0026lt;b.size();i++){ if(a[cc]==b[i])cnt++,cc++; } if(cnt==a.size()){ cout\u0026lt;\u0026lt;\u0026#34;similar\\n\u0026#34;; } else cout\u0026lt;\u0026lt;\u0026#34;not similar\\n\u0026#34;; } else if(a.size()-1==b.size()){ swap(a,b); for(int i=0;i\u0026lt;b.size();i++){ if(a[cc]==b[i])cnt++,cc++; } if(cnt==a.size()){ cout\u0026lt;\u0026lt;\u0026#34;similar\\n\u0026#34;; } else cout\u0026lt;\u0026lt;\u0026#34;not similar\\n\u0026#34;; } else{ cout\u0026lt;\u0026lt;\u0026#34;not similar\\n\u0026#34;; } } return 0; } ","date":"28 April 2024","externalUrl":null,"permalink":"/20240428191300.html/","section":"Posts","summary":"","title":"题解：B3958 [GESP202403 四级] 相似字符串","type":"posts"},{"content":"","date":"27 April 2024","externalUrl":null,"permalink":"/tags/%E8%AE%B0%E5%BF%86%E5%8C%96%E6%90%9C%E7%B4%A2/","section":"Tags","summary":"","title":"记忆化搜索","type":"tags"},{"content":" 思路： # 先写出暴力的搜索，然后改为记忆化搜索。\n暴力的搜索就是暴力向下搜 $n$ 减去 $a$ 和 $n$ 减去 $b$ 的游戏操作序列的总数，暴力搜索代码如下。\nlong long dfs(long long x){ if(x\u0026lt;=c)return 1; return (dfs(x-a)%mod+dfs(x-b)%mod)%mod; } 然后改为记忆化搜索，定义一个数组存结果，如果现在搜索的这个数已经被搜索过了就自己访问数组，如果没有就先搜索，搜索完后赋值，这样就可以防止重复地搜索一个数，记忆化搜索代码如下。\nlong long dfs(long long x){ if(x\u0026lt;=c)return 1; if(an[x]!=0)return an[x];// 如果有值直接返回 return an[x]=(dfs(x-a)%mod+dfs(x-b)%mod)%mod;// 如果没有进行搜索并赋值 } 完整代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; long long an[200005]={0}; long long n,c,b,a; const int mod=1e9+7;// 模 1e9+7 long long dfs(long long x){ if(x\u0026lt;=c)return 1; if(an[x]!=0)return an[x];// 如果有值直接返回 return an[x]=(dfs(x-a)%mod+dfs(x-b)%mod)%mod;// 如果没有进行搜索并赋值 } int main(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;a\u0026gt;\u0026gt;b\u0026gt;\u0026gt;c; cout\u0026lt;\u0026lt;dfs(n); return 0; } ","date":"27 April 2024","externalUrl":null,"permalink":"/20240427165900.html/","section":"Posts","summary":"","title":"题解：P10376 [GESP202403 六级] 游戏","type":"posts"},{"content":" 思路： # 考虑到 $n$ 的最大值只有九，所以可以使用 dfs 算法枚举组合方案，把每一头牛的放置顺序存入一个数组中，当把所有的牛都放好后，算出需要的牛棚，最后输出最小值就可以了。\n如何算出需要的牛棚，先画张图。\n观察这张图，可以分四种情况。\n第一种情况，上一头牛的 $b$ 大于现在这头牛的 $a$，那么距离是上一头牛的 $b$，如下图。\n第二种情况，上一头牛的 $b$ 等于现在这头牛的 $a$，那么这两个数都可以是距离，如下图。\n第三种情况，上一头牛的 $b$ 小于现在这头牛的 $a$，那么距离是这头牛的 $a$，如下图。\n第四种情况，没有上一头牛，也就是第一头牛，没有距离，循环直接从第二个放入牛的开始，如下图。\n通过上述分析可以看出，距离是上一头牛的 $b$ 与这头牛的 $a$ 中的最大值。\n需要注意的是求完距离后要考虑这一头牛也需要一个牛棚，所以每次求出距离后还要加一才可以算出需要的牛棚，要提前处理第一头牛，可以直接将求需要的牛棚的变量赋值为一。\n完整代码带注释： # 代码与思路完全相同。\n#include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; struct node{ int a,b; }cows[10];// 定义结构体 cows 其实可以不用结构体因为刚开始想的是排序所以用的是结构体但后面不知道怎么排也不想改就这样了 int n,minn=INT_MAX;// 定义把 minn 赋值为 int 的最大值 bool bj[10];// 标记数组 int fzsx[10];// 存放置顺序的数组 void add(){ int sum=1;// 定义 sum 赋值为 1 因为第一头牛也需要一个牛棚 for(int i=2;i\u0026lt;=n;i++){// 从 2 开始因为我们求的是这头牛和上一头牛的距离 sum+=max(cows[fzsx[i]].b,cows[fzsx[i-1]].a);// 求这头牛和上一头牛的距离 sum+=1;// 为什么要加 1 因为上面只是求出了距离这头牛也要一个牛棚 } minn=min(minn,sum);// 取最小值 return ; } void dfs(int x){// 通过 dfs 求出组合方案 if(x-1==n){// 为什么要 x-1==n 因为我们是从第一个开始搜的当搜完最后一个时再次调用 dfs 时 x 会比 n 大 1 add();// 调用 add 函数 return ;// 返回空因为 dfs 是 void 类型的 } for(int i=1;i\u0026lt;=n;i++){ if(!bj[i]){ bj[i]=1;// 标记 fzsx[x]=i;// 记录 dfs(x+1);// 求下一个数 bj[i]=0;// 还原 } } } int main(){ cin\u0026gt;\u0026gt;n;// 输入 n for(int i=1;i\u0026lt;=n;i++){// 输入 a cin\u0026gt;\u0026gt;cows[i].a; } for(int i=1;i\u0026lt;=n;i++){// 输入 b cin\u0026gt;\u0026gt;cows[i].b; } dfs(1);//从第一个开始找 cout\u0026lt;\u0026lt;minn\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;// 输出最小值 return 0; } ","date":"26 April 2024","externalUrl":null,"permalink":"/20240426184400.html/","section":"Posts","summary":"","title":"题解：P10377 [GESP202403 六级] 好斗的牛","type":"posts"},{"content":"","date":"25 April 2024","externalUrl":null,"permalink":"/tags/%E7%B4%A0%E6%95%B0%E5%88%A4%E6%96%AD%E8%B4%A8%E6%95%B0%E7%AD%9B%E6%B3%95/","section":"Tags","summary":"","title":"素数判断,质数,筛法","type":"tags"},{"content":" 思路： # 定义一个数组存这个数最大的质因数。\n如何存，枚举每一个小于 $n$ 的质数，再枚举它的倍数，但是在枚举倍数时要注意如果第二个数字大于了第一个数字且第二个数字是质数那么先跳过，且第一个数字乘第二个数字要是大于 $n$ 那么退出这一层循环写进判断条件里，代码如下。\nfor(long long i=2;i\u0026lt;=n;i++){ if(zs(i))//注意 i 要是质数 for(long long j=1;j*i\u0026lt;=n;j++){//枚举 i 的倍数 if(j\u0026gt;i\u0026amp;\u0026amp;zs(j))continue;// 如果 j 大于 i 且 j 是质数 那么说明 i*j 这个数的最大质因数不是 i 直接跳过后面再赋值 bj[j*i]=i; //cout\u0026lt;\u0026lt;i*j\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;// 测试 } else continue;//i 不是质数跳过 } 标记好了后，定义一个存的变量变量赋值为一，因为虽然一没有质因数但是还是要加上，然后枚举从二到 $n$ 中有多少个的最大质因数是小于等于 $B$ 的就可以了，为什么从二开始枚举因为一已经在定义时加上了，代码如下。\nlong long ans=1; for(long long i=2;i\u0026lt;=n;i++){ if(bj[i]\u0026lt;=b)ans++/*,cout\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;*/; } 然后输出就可以了。\n完整代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; long long n,b,ans=1;// 定义移到主函数外了 long long bj[1000099]; bool zs(long long x){// 判断质数 if(x==1)return 0; if(x==2||x==3)return 1; if(x%6!=1\u0026amp;\u0026amp;x%6!=5)return 0; for(int i=5;i*i\u0026lt;=x;i+=6)if(x%i==0||x%(i+2)==0)return 0; return 1; } int main(){ cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;b;// 输入 for(long long i=2;i\u0026lt;=n;i++){ if(zs(i)) for(long long j=1;j*i\u0026lt;=n;j++){ if(j\u0026gt;i\u0026amp;\u0026amp;zs(j))continue; bj[j*i]=i; //cout\u0026lt;\u0026lt;i*j\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } else continue; } for(long long i=2;i\u0026lt;=n;i++){ if(bj[i]\u0026lt;=b)ans++/*,cout\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;*/; //if(bj[i]==0)cout\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;// 测试 } cout\u0026lt;\u0026lt;ans;// 输出 return 0; } ","date":"25 April 2024","externalUrl":null,"permalink":"/20240425183900.html/","section":"Posts","summary":"","title":"题解：B3969 [GESP202403 五级] B-smooth 数","type":"posts"},{"content":"","date":"22 April 2024","externalUrl":null,"permalink":"/tags/%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F/","section":"Tags","summary":"","title":"计数排序","type":"tags"},{"content":" 思路： # 规律：观察后发现答案是每种邮票的张数除以现在的人数后省去小数部分后再次乘以人数。\n每种邮票的张数除以现在的人数后省去小数部分就是可以分成几组，组不能是小数，因为题中说申请人都必须从该城市获得相同数量的邮票所以不能分开。\n再乘以人数就是求分出了几张邮票。\n一开始想用桶但发现数据太大了开不了数组，所以用了 map 但是用了 map 后发现不能满分于是观察了下发现只要有一个的邮票发出总和为零时，后面的也都是零，这样过后发现还是不可以满分，再次观察题目发现是不需要排序的，所以把 map 改成 unordered_map 就这样了吗，没有，再次观察可以发现当一个邮票的张数已经小于现在的人数了，那么不管怎么样，后面都不可以让答案增加了，所以直接删去，这样就可以了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; unordered_map\u0026lt;int,int\u0026gt;mp; int n; int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin\u0026gt;\u0026gt;n; for(int i=1;i\u0026lt;=n;i++){ int a;cin\u0026gt;\u0026gt;a; mp[a]++; } bool s0=0;// 特判 0 for(int i=1;i\u0026lt;=n;i++){ long long sum=0; if(s0){// 特判 0 cout\u0026lt;\u0026lt;\u0026#34;0 \u0026#34;;continue; } auto it=mp.begin(); while(it!=mp.end()){ sum+=(it-\u0026gt;second)/i*i; if((it-\u0026gt;second)\u0026lt;i)mp.erase((it++));// 直接删去 else it++; } cout\u0026lt;\u0026lt;sum\u0026lt;\u0026lt;\u0026#34; \u0026#34;; if(sum==0)s0=1;// 特判 0 } return 0; } ","date":"22 April 2024","externalUrl":null,"permalink":"/20240422224600.html/","section":"Posts","summary":"","title":"题解：P10355 [PA2024] Znaczki pocztowe","type":"posts"},{"content":" 思路： # 使用结构体快排，自己写判断函数，按照题目给出的进行判断，判断后历遍一次结构体数组，把学生的排名赋值，然后按照输入的顺序再排序一次最后输出就可以了。\n如何赋值排名，如果和上一个人的排名是相同的就赋值为上一个人的排名，如果不是直接赋值为 $i$。\n代码： # // 以下代码与思路完全相同 // 本代码已经提交测试并且已经 AC #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; int n; struct node{ long long sum/* 总和 */,chinese/* 语文 */,math/* 数学 */,english/*english 可以不定义在结构体里 */; long long pm/* 排名 */,bh/* 编号 */; }stud[10002]; bool cmp(node x,node y){// 排序排名降序 if(x.sum!=y.sum)return x.sum\u0026gt;y.sum;// 判断总分 else{// 如果总分相同 if(x.chinese+x.math!=y.chinese+y.math)return x.chinese+x.math\u0026gt;y.chinese+y.math;// 比较语文和数学两科的总分 else{ if(max(x.chinese,x.math)!=max(y.chinese,y.math))return max(x.chinese,x.math)\u0026gt;max(y.chinese,y.math);//比较语文和数学两科的最高分 else{ // 并列 return 0;// 可以 return 1 也可以 return 0 } } } } bool cmp1(node x,node y){// 按编号复原升序 return x.bh\u0026lt;y.bh;// 按编号复原 } int main(){ cin\u0026gt;\u0026gt;n; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;stud[i].chinese\u0026gt;\u0026gt;stud[i].math\u0026gt;\u0026gt;stud[i].english,stud[i].sum=stud[i].chinese+stud[i].math+stud[i].english/* 输入并计算总和 */,stud[i].bh=i/* 赋值编号 */; sort(stud+1,stud+1+n,cmp);// 排序排名 int cnt=1;// 上一个人的排名 for(int i=1;i\u0026lt;=n;i++){ if(stud[i].sum==stud[i-1].sum// 判断总分 \u0026amp;\u0026amp;stud[i].chinese+stud[i].math==stud[i-1].chinese+stud[i-1].math// 判断语文和数学两科的总分 \u0026amp;\u0026amp;max(stud[i].chinese,stud[i].math)==max(stud[i-1].chinese,stud[i-1].math)){// 比较语文和数学两科的最高分 stud[i].pm=cnt;// 如果都相同那么说明并列 } else{ cnt=i;// 将名次赋值为 i stud[i].pm=cnt;// 赋值排名 } //cout\u0026lt;\u0026lt;stud[i].bh\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;stud[i].sum\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;stud[i].chinese+stud[i].math\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;stud[i].chinese\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;stud[i].math\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;; } sort(stud+1,stud+1+n,cmp1);// 还原编号 for(int i=1;i\u0026lt;=n;i++)cout\u0026lt;\u0026lt;stud[i].pm\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;// 输出排名 return 0; } ","date":"22 April 2024","externalUrl":null,"permalink":"/20240422192800.html/","section":"Posts","summary":"","title":"题解：B3968 [GESP202403 五级] 成绩排序","type":"posts"},{"content":" 思路： # 先通过此程序列出部分立方根向下取整的情况。\n#include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; long long s[20000000]; long long jia(long long x,long long y){ long long sum=0; for(int i=x+1;i\u0026lt;=y;i++)sum+=cbrt(i); return sum; } int main(){ long long x,y=0; long long last=0,las=0; for(int i=1;i\u0026lt;=1e6;i++){ s[i]=s[i-1]+cbrt(i); if(s[i]-s[i-1]!=last)cout\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;s[i]\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;,last=s[i]-s[i-1]; } return 0; } 可以发现每次当 $i$ 为某数的立方时，立方根向下取整的结果会增加一。\n所以我们可以定义一个变量 $l$ 表示现在历遍的立方根，这样我们就不用从一历遍到十的十二次方了，只用历遍到十的十二次方的立方根了。\n然后是如何计算，第一，当 $l+1$ 的立方也就是下一个立方的值大于我们输入的 $x+1$ 时，我们取较小的数，第二，当 $l$ 的立方也就是现在的立方的值大于上一个输入的 $x$ 的值时，我们在两个数取较大值，然后将 $l+1$ 的立方和 $x+1$ 的较小值与 $l$ 的立方和上一个 $x$ 的较大值相减，这样子做是为了防止多算和少算，然后总和加上就行了，循环条件是 $l$ 的立方小于等于现在的 $x$。\n代码： # 代码中的注释和思路里写的是一样的。\n#include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; #define ull unsigned long long// 无根号 long long ull T,x,l; ull f(ull n){// 计算立方 return n*n*n; } int main(){ cin\u0026gt;\u0026gt;T; ull l=1,sum=0,last=0; for(ull i=1;i\u0026lt;=T;i++){ cin\u0026gt;\u0026gt;x; while(f(l)\u0026lt;=x){//也可以改成 for 循环 sum+=l*((min(f(l+1),x+1))-max(f(l),last+1)); //f(l+1) 是下一个立方的值与 x+1 取最小值约等于特判了如果下一个立方大于 x 的情况 //max(f(l),last+1) 是取现在的立方的值与上一个的 x+1（last+1） 中的最大值 //min((l+1)*(l+1)*(l+1),x+1))-max(l*l*l,last+1) 是为了防止少算和多算 l++;// 每次 l++ } l--;// 因为最后一次 ++ 的 l 是不满足的没有计算所以要 -- cout\u0026lt;\u0026lt;sum\u0026lt;\u0026lt;\u0026#34;\\n\u0026#34;;// 输出 last=x;// 赋值 } return 0;// return 好习惯听说比赛里没有爆 0 } ","date":"21 April 2024","externalUrl":null,"permalink":"/20240421210600.html/","section":"Posts","summary":"","title":"题解：P10373 [AHOI2024 初中组] 立方根","type":"posts"},{"content":" 思路： # 按照题目模拟，题目要求我们判断加法等式是否成立。\n那么如何输入，可以先输入一个数字，再输入字符，再输入数字，再输入字符，再输入数字，然后判断第一个数字加第二个数字是否等于第三个数字就可以了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; int T,a,b,c; int main(){ cin\u0026gt;\u0026gt;T;char fuh; while(T--){ cin\u0026gt;\u0026gt;a\u0026gt;\u0026gt;fuh\u0026gt;\u0026gt;b\u0026gt;\u0026gt;fuh\u0026gt;\u0026gt;c; if(a+b!=c)cout\u0026lt;\u0026lt;\u0026#34;Wrong!\\n\u0026#34;; else cout\u0026lt;\u0026lt;\u0026#34;Right!\\n\u0026#34;; } return 0; } ","date":"21 April 2024","externalUrl":null,"permalink":"/20240421163000.html/","section":"Posts","summary":"","title":"题解：P10372 [AHOI2024 初中组] 家庭作业","type":"posts"},{"content":" 思路： # 观察题目描述发现当 $h$ 等于三时，一共分成了三行。\n先看第一行，打乱前的第一第五第九个字符分别变成了打乱后的第一二三个字符，相邻两个字符之间原本相差 $h-1$ 的两倍。\n再看第二行，首行和尾行的间隔依旧不变，假设列数为 $i$，会有两种情况：\n若当前数为第二行的奇数个字符的时候，下一个字符是 $h-i$ 的两倍。 若当前数为第二行的偶数个字符的时候，下一个字符是 $i-1$ 的两倍。 第 $h$ 行与第一行是一样的。\n再画个只有数字的图来验证我们的思路。\n观察后我们可以发现，当 $h$ 为三时，一共分成了三行，打乱前的一、五、九分别变成了打乱后的第一、二、三个数，相邻两个数之间相差四也就是 $h-1$ 的两倍，第 $h$ 行与第一行相同。再看第二行你会发现，首行和尾行的间隔不变假设列数为 $i$，当前数为第二行的第奇数个数的时候，下一个数字是 $h-i$ 的两倍，若当前数为第二行的第偶数个数的时候，下一个数字是 $i-1$ 的两倍。而且每一行的第一个数字就是这一行的行数。\n与我们上面的思路相同，所以可以写成代码。\n完整代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; string s; char ans[100080]/* 存答案 */; int han=0/* 第几行 */; int h,len,pd1,pd2/* 用来判断现在的位置的字符是奇数还是偶数 */; int check(){// 如思路 if(han==1||han==h)return pd1+(h-1)*2; else{ if(pd2%2==1)return pd1+(h-han)*2; else return pd1+2*(han-1); } } int main(){// 如思路 cin\u0026gt;\u0026gt;h; cin\u0026gt;\u0026gt;s; len=s.size(); s=\u0026#34; \u0026#34;+s; han=pd1=pd2=1; for(int i=1;i\u0026lt;=len;i++){ ans[pd1]=s[i]; pd1=check(); pd2++; if(pd1\u0026gt;len){ han++; pd1=han; pd2=1; } } for(int i=1;i\u0026lt;=len;i++)cout\u0026lt;\u0026lt;ans[i]; return 0; } ","date":"6 April 2024","externalUrl":null,"permalink":"/20240406145600.html/","section":"Posts","summary":"","title":"题解：P10312 [SHUPC 2024] 栅栏密码","type":"posts"},{"content":" 思路： # 通过画图可知编号为 $i$ 的人的正对面的人的编号为 $i+n/2$，前提是 $i$ 小于等于 $n$ 的一半。\n{% image https://cdn.luogu.com.cn/upload/image_hosting/7a6jdljw.png %}\n得知了编号为 $i$ 的人和他的正对面的人的编号就可以直接判断他们帽子的号码是不是相同的就可以了。\n已提交测试。\n完整代码： # #include\u0026lt;bits/stdc++.h\u0026gt; #define LL long long #define CPPname using namespace std CPPname; LL n; const int MAX=1000010; LL a[MAX]; int main(){ cin\u0026gt;\u0026gt;n; LL ans=0; for(int i=1;i\u0026lt;=n;i++)cin\u0026gt;\u0026gt;a[i]; const int c=n/2; for(int i=1;i\u0026lt;=n/2;i++){ if(a[i]==a[i+c])ans+=2; //cout\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;i+n/2\u0026lt;\u0026lt;\u0026#34; \u0026#34;; } cout\u0026lt;\u0026lt;ans; return 0; } ","date":"30 March 2024","externalUrl":null,"permalink":"/20240330224300.html/","section":"Posts","summary":"","title":"题解：P10295 [CCC 2024 S1] Hat Circle","type":"posts"},{"content":" 思路： # 先输入，然后模拟，当 Yobi 的大小大于等于 Dusa 时输出 Dusa 现在的大小，并结束。\n因为题目没有告诉我们有几个 Yobi 所以这样输入：\nwhile(cin\u0026gt;\u0026gt;a[cnt++]); 代码： # #include\u0026lt;bits/stdc++.h\u0026gt; #define LL long long #define CPPname using namespace std CPPname; LL Dusa; int cnt=1,a[200010]; int main(){ int d;cin\u0026gt;\u0026gt;d; Dusa=d; while(cin\u0026gt;\u0026gt;a[cnt++]); for(int i=1;i\u0026lt;cnt;i++){ if(Dusa\u0026gt;a[i])Dusa+=a[i]; else{ cout\u0026lt;\u0026lt;Dusa; return 0; } } return 0; } ","date":"30 March 2024","externalUrl":null,"permalink":"/20240330220100.html/","section":"Posts","summary":"","title":"题解：P10291 [CCC 2024 J2] Dusa And The Yobis","type":"posts"},{"content":" 思路： # 使用桶进行储存，然后从最大的分数从小历遍，当现在是第三个有值的，这就是铜牌，然后输出分数和人数就可以了。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; #define LL long long #define CPPname using namespace std CPPname; int tong[76],n,tamp; int main(){ cin\u0026gt;\u0026gt;n; for(int i=1;i\u0026lt;=n;i++){ cin\u0026gt;\u0026gt;tamp; tong[tamp]++;// 使用桶进行储存 } tamp=0;//计算是第几大的 for(int i=75/* 题目中保证每个分数在 75 以下 */;i\u0026gt;=0/* 题目中保证每个分数在 0 以上 */;i--){ if(tong[i]!=0)tamp++; if(tamp==3){ cout\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;tong[i]; return 0; } } return 0; } ","date":"30 March 2024","externalUrl":null,"permalink":"/20240330215300.html/","section":"Posts","summary":"","title":"题解：P10292 [CCC 2024 J3] Bronze Count","type":"posts"},{"content":" 思路： # 先看题判断其中重的字母和轻的字母是否满足交替出现，由此我们可以得出要先求出是奇数位的字母是重的，还是偶数位的字母重的。\n注意，称一个字母是重的当且仅当它在字符串中出现了超过一次，是在字符串里出现了几次，而不是在它这次出现前出现了几次。\n求出是奇数位的字母是重的，还是偶数位的字母重的，就好写了，只需要在判断的时候看现在历遍的字母的下标是不是需要重的，如果在不需要重的时候重了或在要重的地方没有重就输出 F 就可以了。\n最后还有一个点，特判当字符串长度为二的时候，直接输出 F 就可以了。\n整体思路就这么简单，但是坑多。\n代码： # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; long long T,N; string a; long long cnt[27]={0}; int main(){ cin\u0026gt;\u0026gt;T\u0026gt;\u0026gt;N; while(T--){ cin\u0026gt;\u0026gt;a; int jo=9; memset(cnt,0,sizeof(cnt)); for(int i=0;i\u0026lt;a.size();i++){ cnt[a[i]-\u0026#39;a\u0026#39;]++; } if(N==2){//特判 cout\u0026lt;\u0026lt;\u0026#34;F\\n\u0026#34;;continue; } else{ bool yes=1; for(int i=0;i\u0026lt;a.size();i++){ if(cnt[a[i]-\u0026#39;a\u0026#39;]\u0026gt;=2)jo=i%2,yes=0; } if(yes){ cout\u0026lt;\u0026lt;\u0026#34;F\\n\u0026#34;; continue; } if(jo==1){ bool y=1; for(int i=0;i\u0026lt;a.size();i++){ if(i%2==1){ if(cnt[a[i]-\u0026#39;a\u0026#39;]\u0026lt;2){ y=0;break; } } else{ if(cnt[a[i]-\u0026#39;a\u0026#39;]\u0026gt;=2){ y=0;break; } } } if(!y)cout\u0026lt;\u0026lt;\u0026#34;F\\n\u0026#34;; else cout\u0026lt;\u0026lt;\u0026#34;T\\n\u0026#34;; } else if(jo==0){ bool y=1; for(int i=0;i\u0026lt;a.size();i++){ if(i%2==0){ if(cnt[a[i]-\u0026#39;a\u0026#39;]\u0026lt;2){ y=0;break; } } else{ if(cnt[a[i]-\u0026#39;a\u0026#39;]\u0026gt;=2){ y=0;break; } } } if(!y)cout\u0026lt;\u0026lt;\u0026#34;F\\n\u0026#34;; else cout\u0026lt;\u0026lt;\u0026#34;T\\n\u0026#34;; } } } } 此代码已提交测试过。\n","date":"29 March 2024","externalUrl":null,"permalink":"/20240329224600.html/","section":"Posts","summary":"","title":"题解：P10296 [CCC 2024 S2] Heavy-Light Composition","type":"posts"},{"content":"","date":"26 March 2024","externalUrl":null,"permalink":"/categories/gesp-%E6%A0%B7%E9%A2%98/","section":"Categories","summary":"","title":"GESP 样题","type":"categories"},{"content":" 思路： # 首先看题，让我们求迷宫 $m$ 可以直接到达其他的迷宫有多少个，有多少迷宫可以直接到达 $m$ 号迷宫，和这些迷宫的总和。\n先看迷宫 $m$ 可以直接到达其他的迷宫，是什么意思呢？\n其实就是让我们看当历遍的迷宫是 $m$ 时有多少个为真。\n由此得出代码：\nfor(int i=1;i\u0026lt;=n;i++){ if(jz[m][i]==1){ // 我们只用查看 m 迷宫有多少个 1 sum1++; } } 然后我们看多少迷宫可以直接到达 $m$ 号迷宫，这又是什么意思呢？\n其实就是查看有多少个迷宫的 $m$ 也就是 $jz[i][m]$ 为真。\n由此得出代码：\nfor(int i=1;i\u0026lt;=n;i++){ if(jz[i][m]==1)sumzj++; } 让我们看一下这两份代码循环的条件是不是相同的？\n是对吧，由此合并两份代码：\nfor(int i=1;i\u0026lt;=n;i++){ if(jz[m][i]==1){ sum1++; } if(jz[i][m]==1)sumzj++; } 最后看这些迷宫的总和，没有坑直接输出就行。\n完整代码 # #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; bool jz[1001][1001]; long long sumzj; long long sum1; int main(){ int n,m; cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=n;i++){ for(int j=1;j\u0026lt;=n;j++){ cin\u0026gt;\u0026gt;jz[i][j]; } } for(int i=1;i\u0026lt;=n;i++){ if(jz[m][i]==1){ sum1++; } if(jz[i][m]==1)sumzj++; } cout\u0026lt;\u0026lt;sum1\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;sumzj\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;sum1+sumzj; return 0; } ","date":"26 March 2024","externalUrl":null,"permalink":"/20240326223000.html/","section":"Posts","summary":"","title":"题解：P10265 [GESP样题 七级] 迷宫统计","type":"posts"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]