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\n";
        else cout << "YES\n";
    }

    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\n",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\n",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\n",res >= 1e18 ? -1 : res);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM