Codeforces Round #719 (Div. 3) 题解

本场链接:Codeforces Round #719 (Div. 3)

闲话

不会F2/G,会了再补

A. Do Not Be Distracted!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int main()
{
    Angel_Dust;
    int T;cin >> T;
    while(T--)
    {
        int n;cin >> n;
        string s;cin >> s;
        s.erase(unique(s.begin(),s.end()),s.end());
        set<char> st;
        bool ok = 1;
        for(auto& v : s)
        {
            if(st.count(v))
            {
                ok = 0;
                break;
            }
            st.insert(v);
        }
        if(!ok) cout << "NO
";
        else cout << "YES
";
    }

    return 0;
}   

B. Ordinary Numbers

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int main()
{
    Angel_Dust;
    int T;cin >> T;
    while(T--)
    {
        ll n;cin >> n;
        ll res = 0;
        forn(i,1,9)
        {
            ll cur = i;
            while(cur <= n)
            {
                ++res;
                cur = cur * 10 + i;
            }
        }
        cout << res << endl;
    }
    return 0;
}   

C. Not Adjacent Matrix

相邻位置的格子数字不能相同,于是不妨把相邻的数字按斜对角放置。特别地,2在最后放置。

可以自己写个checker

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 105;
const int dx[] = {-1,0,1,0},dy[] = {0,1,0,-1};
int ans[N][N];

int main()
{

    int T;cin >> T;
    while(T--)
    {
        int n;scanf("%d",&n);
        if(n == 1)  puts("1");
        else if(n == 2) puts("-1");
        else
        {
            forn(i,1,n)
            {
                if(i == 1)
                {
                    ans[1][1] = 1;
                    continue;
                }
                if(i == 2)  ans[2][1] = 3;
                else ans[i][1] = ans[1][i - 1] + 1;
                int x = i,y = 1;
                --x;++y;
                while(x > 1)
                {
                    ans[x][y] = ans[x + 1][y - 1] + 1;
                    --x;++y;
                }
                ans[x][y] = ans[x + 1][y - 1] + 1;
            }

            forn(i,2,n)
            {
                ans[n][i] = ans[i - 1][n] + 1;
                int x = n,y = i;
                --x;++y;
                while(x > i)
                {
                    ans[x][y] = ans[x + 1][y - 1] + 1;
                    --x;++y;
                }
                ans[x][y] = ans[x + 1][y - 1] + 1;
            }
            ans[n][n] = 2;
            
            forn(i,1,n)
            {
                forn(j,1,n) printf("%d ",ans[i][j]);
                puts("");
            }
        }
    }
    return 0;
}   

D. Same Differences

等价于求((i,j))满足(i < j,a_j - j = a_i - i)。维护([i + 1,n])(a_i-i)的取值,枚举求解即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 2e5+7;
int a[N];

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n;scanf("%d",&n);
        map<int,int> cnt;
        forn(i,1,n) scanf("%d",&a[i]),++cnt[a[i] - i];
        
        ll res = 0;
        forn(i,1,n)
        {
            if(--cnt[a[i] - i] == 0)    cnt.erase(a[i] - i);
            if(cnt.count(a[i] - i)) res += cnt[a[i] - i];
        }

        printf("%lld
",res);
    }
    return 0;
}   

E. Arranging The Sheep

显然所有的羊最后应该都有一个基准,且这个基准位置一开始一定就是羊,否则的话可以平移等价。那么问题变成枚举作为基准的羊的位置,并计算其他羊走过来的距离,计算即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 1e6+7;
char s[N];
ll sum[N];

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n;scanf("%d",&n);getchar();
        scanf("%s",s + 1);
        vector<int> pos;
        forn(i,1,n)
        {
            sum[i] = sum[i - 1];
            if(s[i] == '*') sum[i] += i,pos.push_back(i);
        }

        ll res = -1;
        for(auto& p : pos)
        {
            int cl = lower_bound(pos.begin(),pos.end(),p) - pos.begin(),
                cr = pos.end() - upper_bound(pos.begin(),pos.end(),p);
            ll cur = 1ll * p * cl - 1ll * cl * (cl - 1) / 2 - sum[p - 1] - cl;
            cur += sum[n] - sum[p] - 1ll * p * cr - 1ll * cr * (cr - 1) / 2 - cr;
            if(res == -1)   res = cur;
            else res = min(res,cur);
        }
        printf("%lld
",res == -1 ? 0 : res);
    }
    return 0;
}   

F1. Guess the K-th Zero (Easy version)

考虑维护一个区间([l,r]),这个区间始终满足:区间内包含第(k)(0)

考虑一个二分框架:设中点(mid),若([l,mid])(0)的数量不够(k)说明第(k)(0)一定在([mid + 1,n])中,问题等价于在([mid + 1,n])中求第(k - cnt)(0)的位置,其中(cnt)([l,mid])(0)的个数。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 1e6+7;
char s[N];
ll sum[N];

int query(int l,int r)
{
    cout << "? " << l << " " << r << endl;
    int res;cin >> res;
    return res;
}

int main()
{
    Angel_Dust;
    int n,t,k;cin >> n >> t >> k;
    int l = 1,r = n;
    while(l < r)
    {
        int mid = l + r >> 1;
        int z = mid - l + 1 - query(l,mid);
        if(z < k)
        {
            k -= z;
            l = mid + 1;
        }
        else    r = mid;
    }

    cout << "! " << l << endl;
    return 0;
}   

F2. Guess the K-th Zero (Hard version)

不难想到一个粗暴的线段树做法:直接把所有的叶子节点交互出来,之后自下而上合并出整个线段树的结构,于是就可以支持所有的询问了。但是这样需要交互(n)次,不能接受。

但是问题的个数至多只有(1e4)个,这个值不和(n)相同意味着这颗线段树有很多的节点是根本不会使用的。如果把整个线段树只保留需要使用的区间,那么需要的交互次数就对应这颗树的节点的个数。考虑证明节点的数量上界:首先不难手推发现(n)次交互之后,如果要形成一颗形态满的二叉树,最多可以有(n)个叶子,所以整个树的大小就是(2^{log(n)} - 1)。因为最多有(1e4)次询问,所以实际上这颗线段树完全长满的部分的节点个数上界是(2^{14} -1)。其次根据(n)的范围可以知道整个的树高不超过(18),那么对于处在([15,18])层的节点,每个节点相当于是上面满的二叉树牵出来的尾巴,每条往下最多拓展(4)次节点,所以整的交互次数上界就是(2^{14} - 1 + 4e4)。在限制的交互次数内。

得到这个结论之后,只需要缓存一下每个区间的值就可以了,也不需要把线段树建出来。注意还有一个条件是每次得到的(0)要转换成(1),把所有受到影响的区间值维护一下即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

map<pii,int> cache;

void push(int u,int l,int r)
{
	--cache[{l,r}];
	if(l == r)	return ;
	int mid = l + r >> 1;
	if(u <= mid)	push(u,l,mid);
	else push(u,mid + 1,r);
}

int main()
{
	Angel_Dust;
	int n,t;cin >> n >> t;
	while(t--)
	{
		int k;cin >> k;
		int l = 1,r = n;
		while(l < r)
		{
			int mid = l + r >> 1;
			pii cur = {l,mid};
			if(!cache.count(cur))
			{
				cout << "? " << l << " " << mid << endl;
				cin >> cache[cur];
				cache[cur] = mid - l + 1 - cache[cur];
			}
			
			int z = cache[cur];
			if(z >= k)	r = mid;
			else l = mid + 1,k -= z;
		}
		cout << "! " << l << endl;
		push(l,1,n);
	}
    return 0;
}

G. To Go Or Not To Go?

结论:只会使用一对传送门。这个题的传送门是几乎没有使用限制的,如果走A->B->C一定不如A->C直接走过去。所以只会使用一对传送门。

剩下的就非常简单了,只有两种情况:要么不用传送门,要么用一对。对于用的一位肯定也是找起点能走到的代价最小的以及从终点走能走到的代价最少的,代价拼在一起即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second

const int N = 2e3+7;
const int dx[] = {-1,0,1,0},dy[] = {0,1,0,-1};
int g[N][N],n,m,w;
ll dist[N][N];
pii q[N * N];

void bfs(int sx,int sy)
{
	forn(i,1,n)	forn(j,1,m)	dist[i][j] = 1e18;
	int hh = 0,tt = -1;
	q[++tt] = {sx,sy};dist[sx][sy] = 0;
	while(hh <= tt)
	{
		auto _ = q[hh++];
		int x = _.x,y = _.y;
		forn(_,0,3)	
		{
			int a = x + dx[_],b = y + dy[_];
			if(a < 1 || a > n || b < 1 || b > m || g[a][b] == -1)	continue;
			if(dist[a][b] > dist[x][y] + w)
			{
				dist[a][b] = dist[x][y] + w;
				q[++tt] = {a,b};
			}
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&w);
	forn(i,1,n)	forn(j,1,m)	scanf("%d",&g[i][j]);
	if(g[1][1] == -1)	return puts("-1"),0;

	bfs(1,1);

	pii minpf = {-1,-1},minpe = {-1,-1};
	ll res = dist[n][m],cur = 0;
	forn(i,1,n)	forn(j,1,m)
	{
		if(g[i][j] <= 0 || dist[i][j] >= 1e18)	continue;
		if(minpf.x == -1 || dist[minpf.x][minpf.y] + g[minpf.x][minpf.y] > dist[i][j] + g[i][j])	minpf = {i,j};
	}
	cur = dist[minpf.x][minpf.y] + g[minpf.x][minpf.y];

	bfs(n,m);
	forn(i,1,n)	forn(j,1,m)
	{
		if(g[i][j] <= 0 || dist[i][j] >= 1e18)	continue;
		if(minpe.x == -1 || dist[minpe.x][minpe.y] + g[minpe.x][minpe.y] > dist[i][j] + g[i][j])	minpe = {i,j};	
	}
	
	if(minpf.x != -1 && minpe.x != -1)	res = min(res,cur + g[minpe.x][minpe.y] + dist[minpe.x][minpe.y]);
	printf("%lld
",res >= 1e18 ? -1 : res);
    return 0;
}

原文地址:https://www.cnblogs.com/HotPants/p/14737337.html