前言
这篇题解大概是我 8 : 00 8:00 8:00 写的,当时感觉自己 AK \texttt{AK} AK 了。 9 : 00 9:00 9:00 这篇题解已经出炉了,检查了一下代码没发现啥问题,最后一题不会对拍,自闭了,如何生成一棵树啊。。。
正文
T1
题目描述
小x有一个整数,他想将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零。他觉得这个问题太简单,就将其抛给了你。
题目分析
开始的时候就开始开 T 1 T1 T1,直接写完交了
模拟一下就好了
题目代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int main(){
int n,ans=0;read(n);
if(n<0){putchar('-');n*=-1;}
while(n){
ans=ans*10+n%10;
n/=10;
}cout<<ans;
return 0;
}
T2
题目描述
小x学了编程,但他吃了饭没事干,于是想自己写一个计算器,计算器上有“+”、“-”、“*”、“/”(整除号)的按键,但他写完以后发现他写错了,看着几百行的程序,他心态崩了,不想调,于是让你给他写一个计算器,来处理一堆正整数的加减乘除运算。
题目分析
T 2 T2 T2 是个模拟题,思路大家应该都清楚,先把数字和符号隔离出来,然后先把乘法和除法算好,次数式子就变成了只有加法和减法的式子,这个没有难度吧。
题目代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
vector<pair<int,char> >q;
vector<pair<int,char> >s;
int work1(){
for(unsigned i=0;i<q.size();i++){
switch(q[i].second){
case '+':s.push_back(q[i]);break;
case '-':s.push_back(q[i]);break;
case '*':s[s.size()-1].first*=q[i].first;break;//算乘
case '/':s[s.size()-1].first/=q[i].first;break;//算除
}
}
int ans=0;
for(unsigned i=0;i<s.size();i++){
if(s[i].second=='+')ans+=s[i].first;
else ans-=s[i].first;
}return ans;
}
int work(){
string st;
cin>>st;
q.push_back(make_pair(0,'+'));//第1个数前面的符合可以看成是+号
for(unsigned i=0;i<st.size();i++)
if(isdigit(st[i]))q[q.size()-1].first=q[q.size()-1].first*10+st[i]-48;
else q.push_back(make_pair(0,st[i]));
cout<<work1()<<endl;
return 0;
}
int main(){
int T;
read(T);
while(T--){
q.clear();s.clear();
work();
}
return 0;
}
T3
题目描述
小x有一张由n行和m列组成的图片。行从上到下从1到n编号,列从左到右从1到m编号。每个单元都涂成黑色或白色。
小x认为这幅画不够有趣。如果一幅画里至少有一个十字架,你会觉得它很有趣。十字由一对数字x和y表示,其中1≤x≤n和1≤y≤m,这样x行中的所有单元格和y列中的所有单元格都涂成黑色。
例如,这些图片中的每个都包含十字架:以下图像不包含十字架:
你有一把刷子和一罐黑漆,所以你可以使这幅画有趣。每分钟你都可以选择一个白色的单元格,然后把它涂成黑色。问题是你最少需要多少次,才能使图片至少包含一个十字?
题目分析
先枚举行,算行需要多少次涂鸦墙,然后看列。
注意一个小细节,行和列有重合的地方。
题目代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
char ch[1010][1010];
int s[1010];
int work(){
int n,m,ans=INT_MAX;read(n);read(m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>ch[i][j];
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
if(ch[i][j]=='.')s[j]++;
for(int i=1;i<=n;i++){
int sm=0,mx=INT_MAX;
for(int j=1;j<=m;j++)
if(ch[i][j]=='.')sm++;
for(int j=1;j<=m;j++)
mx=min(mx,s[j]-(ch[i][j]=='.'));//这里!
ans=min(ans,mx+sm);
}cout<<ans<<endl;
return 0;
}
int main(){
int T;read(T);
while(T--){
memset(s,0,sizeof(s));
work();
}
return 0;
}
T4
题目描述
小x自娱自乐,他在黑板上写下了一行共n个数字,分别是1,2k,22k…2(n-1)k。现在,他想先擦去黑板上第1个数字,然后擦去黑板上剩余数字中的第2个数字,再擦去剩余数字中第3个数字……直到小x无法继续擦去。现在你想知道,算法停止后第x个剩余数的值是多少。
题目分析
首先,这个擦数会令我们感到不爽,于是,我们看看会取哪些数字。
假设有 10 10 10 个数: { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } \{ 1,2,3,4,5,6,7,8,9,10\} {1,2,3,4,5,6,7,8,9,10}
- 擦 1 1 1,序列变成 { 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } \{ 2,3,4,5,6,7,8,9,10\} {2,3,4,5,6,7,8,9,10}
- 擦 3 3 3,序列变成 { 2 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } \{ 2,4,5,6,7,8,9,10\} {2,4,5,6,7,8,9,10}
- 擦 5 5 5,序列变成 { 2 , 4 , 6 , 7 , 8 , 9 , 10 } \{ 2,4,6,7,8,9,10\} {2,4,6,7,8,9,10}
- 擦 7 7 7,序列变成 { 2 , 4 , 6 , 8 , 9 , 10 } \{ 2,4,6,8,9,10\} {2,4,6,8,9,10}
- 擦 9 9 9,序列变成 { 2 , 4 , 6 , 8 , 10 } \{ 2,4,6,8,10\} {2,4,6,8,10}
- 无法继续进行,操作结束
此时很明显的规律如下:所有的奇数会被擦掉,所有的偶数会被留下。
再拓展一下,最后剩下的数的个数 = = = 偶数的个数 = = = ⌊ n 2 ⌋ \lfloor \frac{n}{2} \rfloor ⌊2n⌋
这样的话就简单了。
首先判断 − 1 -1 −1,直接看 ⌊ n 2 ⌋ \lfloor \frac{n}{2} \rfloor ⌊2n⌋ 是否 < < < x x x。
然后第 x x x 个数,显然等于 2 ( x ∗ 2 − 1 ) k 2^{(x*2-1)k} 2(x∗2−1)k。
题目代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
void write(ll x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int mod=19260817;
ll pw(ll x,ll y){
ll ans=1;
while(y){
if(y&1)ans=ans*x%mod;
y>>=1;
x=x*x%mod;
}return ans;
}
int work(){
ll n,x,k;read(n);read(x);read(k);
n/=2;
if(x>n)return puts("-1"),0;
write(pw(2,(x*2ll-1ll)*k));
puts("");
return 0;
}
int main(){
int T;read(T);
while(T--){work();}
return 0;
}
T5
题目描述
小x管理着N 个城市,这些城市之间有N-1条道路将他们连接起来,小x发现任何两个城市都能够直接或者间接通过道路连接。
虽然所有城市是可以互相到达的,但是他担心有恐怖分子破坏这片区域的安定,破坏道路,于是小x又秘密地修建了 M 条小路来连接。
果不其然,最近一个奇怪的组织意欲破坏道路,请你帮小x想想办法,如果城市 A 到城市 B 的直连道路被破坏,从 A 走到 B 的所有路径中,经过小路的距离最少是多少?
题目分析
n n n 个点, n − 1 n-1 n−1 条边,整张图连通,这不是树吗?
首先,树有一个性质——砍掉任意的一条边,就不联通。
我们以一张图为例。
- 橙色的边表示删除的边。
- 红色的边表示树上的边。
- 紫色的边表示新加的边。
先说一个结论,如果有解的话,新加的边用且仅用一次。
为什么?
首先一条边被删除后,一棵树变成了两棵树,起点和终点必定在不同的树里,所以我们需要一座桥连接两棵树。
那为什么不会使用多条新加的边呢?因为在一棵树内,任意两点均能相互达到。
知道这个就好办了。
我们先 d f s dfs dfs 找连通块,遍历其中一棵子树。
我们再去找这棵子树连向另外一棵子树的新加边,并从中找到边权最小的作为我们的答案。
题目代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
const int MAXN=1e4+10;
int n,m,x[MAXN],y[MAXN],h[MAXN];
vector<int>v[MAXN];
vector<pair<int,int> >e[MAXN];
vector<int>v1;
void dfs(int x,int fa,int z){
v1.push_back(x);h[x]=1;
for(auto i:v[x])
if(i!=fa&&i!=z)dfs(i,x,z);
}
int main(){
read(n);read(m);
for(int i=1;i<n;i++){
read(x[i]),read(y[i]);
v[x[i]].push_back(y[i]);
v[y[i]].push_back(x[i]);
}
for(int i=1;i<=m;i++){
int x,y,z;read(x);read(y);read(z);
e[x].push_back(make_pair(y,z));
e[y].push_back(make_pair(x,z));
}
for(int i=1;i<n;i++){
v1.clear();memset(h,0,sizeof(h));
dfs(x[i],0,y[i]);
int ans=INT_MAX;
for(auto j:v1)
for(auto k:e[j])
if(!h[k.first])ans=min(ans,k.second);
if(ans==INT_MAX)puts("-1");
else cout<<ans<<endl;
}
return 0;
}
时间复杂度
这个代码刚写完时,我吓了一跳,这不是 O ( n 2 m ) O(n^2m) O(n2m) 吗?
过了一会儿,我想了想,发现这其实是 O ( n m + n 2 ) O(nm+n^2) O(nm+n2) 的,而且跑不满。
为什么?
我们看 35 ∼ 37 35\sim 37 35∼37 行代码,这里并不是 O ( n m ) O(nm) O(nm) 的,而是 O ( m ) O(m) O(m) 的,因为新加的一条边最多被访问 2 2 2 次( 2 2 2 个端点各访问一次),时间复杂度是忽略常数的,并且这里是真的跑不满。
但愿这题不
T
L
E
TLE
TLE (要
T
L
E
TLE
TLE 也只会因为那个
m
e
m
s
e
t
memset
memset 可能
T
T
T),不
W
A
WA
WA(没道理
W
A
WA
WA),不
R
E
RE
RE(没资格
R
E
RE
RE),不
U
K
E
UKE
UKE(没可能
U
K
E
UKE
UKE),不
M
L
E
MLE
MLE(
v
e
c
t
o
r
vector
vector 帮我定着呢),不
C
E
CE
CE(这个网站开 c++11
,我试过)。(所以不可能不
A
C
AC
AC !但愿吧。。。)
后记
由于这篇题解是比赛的时候写的,所以分数暂时还不知道,但愿能做对的都做对,最好能 A K AK AK。