天天快乐编程2020年OI集训队 训练6题解
本次训练题目为字符串、广搜和树状数组。
1.4858: 多项式输出
NOIP2009普及组T1
真*模拟题,可以考虑下所有情况,第一项没有加,可以是减,如果是最后一项(常数项,直接输出即可),如果绝对值不是1,要变为整数,1次直接输出x
wch的代码
#include<bits/stdc++.h>
using namespace std;
int a[105];
int main()
{
int n;
cin>>n;
for(int i=0;i<=n;i++)
{
cin>>a[i];
if(a[i]==0) continue;
if(a[i]>0 && i!=0)
cout<<"+";
if(a[i]<0)
cout<<"-";
if(i==n)
{
cout<<abs(a[i]);
break;
}
if(abs(a[i])!=1)
cout<<abs(a[i]);
if(i==n-1)
cout<<"x";
if(a[i]!=0 && i!=n-1)
{
cout<<"x^"<<n-i;
}
}
return 0;
}
2.3328: isbn
NOIP2008普及组T1
又是一个模拟题,由于有个X,我们可以直接把它变成10,然后注意下过程的相关数据即可。
#include <bits/stdc++.h>
using namespace std;
char dp[1005];
int a[6]={1,2,3,5,10,20},b[6];
int main()
{
//ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int a,b,c,e;
char d;
while(cin>>a>>d>>b>>d>>c>>d>>d)
{
if(a==0&&b==0&&c==0&&d=='0')break;
if(d=='X')e=10;
else e=d-'0';
int ans=0,cs=9,t;
t=c;
for(int i=0;i<5;i++)
{
ans=(ans+cs*(t%10))%11;
t/=10,cs--;
}
t=b;
for(int i=0;i<3;i++)
{
ans=(ans+cs*(t%10))%11;
t/=10,cs--;
}
ans=(ans+a)%11;
if(ans==e)
cout<<"Right\n";
else
{
if(ans==10)
printf("%d-%03d-%05d-X\n",a,b,c);
else printf("%d-%03d-%05d-%d\n",a,b,c,ans);
}
}
return 0;
}
3.4849: Jam的计数法
NOIP2006普及组T3
这道题其实是输出一个字符串(可以把a看成1,b看成2,以此类推……)满足进制的后面连续5个字符串,这个进制是t+1进制(逢t进一),最小数是s(s没什么用)。不过有一个很奇怪的规定,就是后面的数一定要比前面的数大。
拿样例来看:bdfij
首先最后一位j已经不能再+1了,那就向前一位i进一,进一后变成j,后面的数要比前面的数大,所以j还要再向f进一,f进一变成g,满足了后面两个数都可以比前一个数大的条件,这时要求和原数只差1的数,倒数第二位就变成g+1=h,最后一位就变成h+1=i,结果就是bdghi。
实现的话就是先把最后一位+1,看有没有超过进制,如果没有,就直接输出,继续下一个+1,如果有,就把前面一位+1,再看有没有超出进制,没有的话把后面的数(最后一位)依次从前往后赋值成前一个数(字符)+1,输出,有的话重复上面的步骤,如果第一位进一满足不了后面的数比前面的数大的条件的话,就终止程序,否则输出5次就行了。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a,b,n,i2=0;
string s;
cin>>a>>b>>n>>s;
for(int i=1;i<=5;i++)
{
s[n-1]++;
for(int q=n-1;;q--)
{
if(s[q]>=b+'a'-i2)
{
s[q-1]++;
if(q==0) return 0;
}
else
{
for(int j=q+1;j<=n-1;j++)
{
s[j]=s[j-1]+1;
}
break;
}
i2++;
}
cout<<s<<endl;
i2=0;
}
return 0;
}
4.4829: 计算器的改良
NOIP2000普及组T1
这个题目需要把带未知数的移动到一边,数字移动到另一边,然后进行求解,需要注意细节很多
我的比较短的代码
#include<stdio.h>
char s[105],c;
int main()
{
scanf("%s",s);
int x=0,n=0,f=1,k=1;
for(int i=0; s[i]; i++)
{
if(s[i]=='=') f=-1,k=1;
else if(s[i]=='-')k=-1;
else if(s[i]=='+')k=1;
else
{
int a=0;
while(s[i]>='0'&&s[i]<='9')a=a*10+s[i++]-'0';
if(s[i]>='a'&&s[i]<='z')
c=s[i],x-=f*k*(a==0?1:a);
else
n+=f*k*a,i--;
}
}
printf("%c=%.3f",c,n*1.0/x);
}
5.4890: 寻找道路
NOIP2014提高组Day2T2
这是图论的题目,可以用BFS解决。
首先,预处理,把每条边反向。
从终点开始bfs,标记从终点开始可以走到的点。
第二步,枚举每一个点,如果这个点没有被标记,则枚举它的每一条出边(反向后的),如果它指向的点被标记,则说明这个被标记的点不合法,删除。
第三步,在合法点上bfs,单源最短路。
#include<bits/stdc++.h>
using namespace std;
const int N = 10005;
vector<int> G[N];
bool v1[N], v2[N];
queue<int> q;
int s, t, ans = 0;
bool a = false;
struct T
{
int pos, step;
};
void bfs1()
{
q.push(t);
v1[t] = true;
while (!q.empty())
{
int x = q.front();
q.pop();
int size = G[x].size();
for (int i = 0; i < size; i++)
{
if (!v1[G[x][i]])
{
v1[G[x][i]] = true;
q.push(G[x][i]);
}
}
}
}
void bfs2()
{
queue<T> q;
T now, next;
now.pos = t;
now.step = 0;
q.push(now);
while (!q.empty())
{
T x = q.front();
q.pop();
now = x;
if (now.pos == s)
{
a = true;
ans = now.step;
return;
}
for (int i = 0; i < G[now.pos].size(); i++)
{
if (!v2[G[now.pos][i]])
{
next.pos = G[now.pos][i];
next.step = now.step + 1;
q.push(next);
}
}
}
}
int main()
{
memset(v1, false, sizeof(v1));
memset(v2, false, sizeof(v2));
int v, l;
cin >> v >> l;
for (int i = 1; i <= l; i++)
{
//反向建图
int x, y;
cin >> x >> y;
G[y].push_back(x);
}
cin >> s >> t;
// bfs 找出没有访问到的节点,然后将该结点前一个节点设置为已访问过
//这样第二次 bfs 就不会访问这个节点
bfs1();
for (int i = 1; i <= v; i++)
{
if (!v1[i])
{
for (int j = 0; j < G[i].size(); j++)
v2[G[i][j]] = true;
}
}
bfs2();
if (a)
cout << ans << endl;
else
cout << "-1\n";
return 0;
}
6.4816: 引水入城
NOIP2010提高组T4
需要使用BFS或DFS和动态规划,纯粹的BFS在提高组出现不多。
第一问很好做,直接BFS求一下最下面一排的店有没有不能被覆盖到的就行了
第二问明显的思路是对第一排每个点进行dfs或bfs,搜出每个点能够覆盖到的区间,再做线段覆盖就行了,这样复杂度很高
我们可以不可以剪枝+贪心覆盖呢,我竟然过了
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x7fffffff, N = 505;
struct T
{
int x, y;
};
struct E
{
int l, r;
} edge[N];
int n, m, a[N][N];
queue<T> q;
int dir[4][2] = {0, -1, 0, 1, -1, 0, 1, 0};
int vis[N][N];
int lm, rm;
int size = 0;
int cmp(E a, E b)
{
if (a.l == b.l)
return a.r < b.r;
return a.l < b.l;
}
int check(T nex, T now)
{
int x = nex.x, y = nex.y;
int nx = now.x, ny = now.y;
if (x < 1 || y < 1 || x > n || y > m || a[nx][ny] <= a[x][y])
return 0;
return 1;
}
void bfs(T st, int o)
{
while (!q.empty())
q.pop();
T now, nex;
q.push(st);
vis[1][o] = o; //这里很巧妙,避免了每次都要重新设为零。
while (!q.empty())
{
now = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
nex.x = now.x + dir[i][0];
nex.y = now.y + dir[i][1];
if (vis[nex.x][nex.y] == o)
continue;
if (check(nex, now))
{
vis[nex.x][nex.y] = o;
q.push(nex);
if (nex.x == n)
{
//到达最后一行
lm = min(lm, nex.y);
rm = max(rm, nex.y);
}
}
}
}
edge[o].l = lm;
edge[o].r = rm;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];
T fir;
for (int i = 1; i <= m; i++)
{
if (a[1][i] >= a[1][i - 1] && a[1][i] >= a[1][i + 1]) //重要剪枝,如果这个比两边都大或者相等才bfs,否则见else,将该边左端点设为INF,都端点设为0。
{
lm = INF;
rm = 0;
fir.x = 1;
fir.y = i;
bfs(fir, i);
}
else
{
edge[i].l = INF;
edge[i].r = 0;
}
}
int cnt = 0;
for (int i = 1; i <= m; i++)
if (vis[n][i] == 0)
cnt++;
if (cnt)
{
cout << 0 << endl
<< cnt;
return 0;
}
cout << 1 << endl;
sort(edge + 1, edge + m + 1, cmp);
edge[1].l = 1;
int now = 0, to = 0, ans = 0;
for (int i = 1; i <= m; i++)
{
if (edge[i].l == INF)
continue;
if (now + 1 >= edge[i].l)
to = max(edge[i].r, to);
else
{
ans++;
now = to;
to = max(to, edge[i].r);
}
}
if (now != m)
ans++;
cout << ans;
return 0;
}
7.5998: 列队
NOIP2017提高组 DAY2 T3
需要使用树状数组或线段树进行求解。
定义第i行为所有的点(i,j),0<j<m
可以发现,每一行是相对独立的,每一次操作只会影响到当前行和最后一列
考虑每一行和最后一列各开一个树状数组,但这样显然会爆空间
实际上,对于没有离队过的点是没必要储存的,可以直接算出编号,
因此只要用vector储存每一行和最后一列后加入的点即可
还需要预处理一个数组d[i]表示第i次询问当前行的离队的点的纵坐标
这个可以离线做出来,然后只需要对最后一列维护一个树状数组即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 300005;
#define lowbit(x) ((x) & (-x))
struct T
{
int p, id;
} A[N];
int n, m, qn, mx, d[N], a[N << 1], q[N][2];
vector<T> t[N];
vector<ll> v[N], lst;
void add(int x, int y)
{
for (; x <= mx; x += lowbit(x))
a[x] += y;
}
int Q(int x)
{
int r = 0;
for (; x; x -= lowbit(x))
r += a[x];
return r;
}
int Find(int R)
{
int tmp = 0;
for (int l = 0, r = mx; l <= r;)
{
int mid = (l + r) >> 1;
if (Q(mid) >= R)
tmp = mid, r = mid - 1;
else
l = mid + 1;
}
return tmp;
}
int main()
{
cin >> n >> m >> qn;
mx = max(n, m) + qn;
for (int i = 1; i <= qn; i++)
{
cin >> q[i][0] >> q[i][1];
if (q[i][1] != m)
t[q[i][0]].push_back({q[i][1], i});
}
for (int i = 1; i <= mx; i++)
add(i, 1);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < t[i].size(); j++)
add(d[t[i][j].id] = Find(t[i][j].p), -1);
for (int j = 0; j < t[i].size(); j++)
add(d[t[i][j].id], 1);
}
ll Ans;
for (int i = 1; i <= qn; i++)
{
int x = Find(q[i][0]);
Ans = (x <= n) ? (ll)x * m : lst[x - n - 1];
add(x, -1);
if (q[i][1] != m)
{
v[q[i][0]].push_back(Ans);
Ans = (d[i] < m) ? (q[i][0] - 1) * 1ll * m + d[i] : v[q[i][0]][d[i] - m];
}
lst.push_back(Ans);
printf("%lld\n", Ans);
}
}
本文来自博客园,作者:暴力都不会的蒟蒻,转载请注明原文链接:https://www.cnblogs.com/BobHuang/p/13765594.html