天天快乐编程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);
    }
}
posted @ 2020-10-03 21:35  暴力都不会的蒟蒻  阅读(287)  评论(0编辑  收藏  举报