Codeforces Round 888 (Div. 3)DEF

发布时间 2023-10-04 12:42:20作者: nannan4128

Codeforces Round 888 (Div. 3)DEF

D. Prefix Permutation Sums

题意:给你一个长度为 \(n - 1\) 的数组,是否能找出一个长度为 \(n\) 的排列,求出这个排列的前缀和,去掉前缀和数组的任意一个元素之后和原来的数组相等。

例如 \([6, 8, 12, 15]\),可以是排列 \([1, 5, 2, 4, 3]\) 的前缀和 \([1, 6, 8, 12, 15]\) 去掉元素 \(1\)

思路:维护差分数组,记录已经有了的,还需要的元素和额外的元素。最后去check是否满足。

因为只删除了一个元素,所以如果额外的元素大于1了肯定是不对的(需要的元素(如果有的的话是2个)一定加起来等于这一个额外的)。当然也有可能删除的是最后一个,那就不存在额外的元素,所以如果没有额外的元素也是正确的。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
ll a[N],d[N];
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        map<ll,bool>hav;

        for(int i = 1;i < n; i++)
            cin>>a[i];
        for(int i = 1;i < n; i++)
            d[i] = a[i]-a[i-1];
        // for(int i = 1;i < n; i++)
        //     cout<<d[i]<<" ";
        // cout<<"\n";
        vector<ll>exa,need;
        for(int i = 1;i < n; i++)
        {
            if(d[i]>=1&&d[i]<=n&&!hav[d[i]])
                hav[d[i]] = true;
            else{
                exa.push_back(d[i]);
            }
        }
        if(exa.empty())
        {
            cout<<"YES\n";
            continue;
        }
        if(exa.size()>1)
        {
            cout<<"NO\n";
            continue;
        }
        for(int i = 1;i <= n; i++)
        {
            if(hav[i])continue;
            need.push_back(i);
        }
        // cout<<"need: ";
        // for(auto x : need)
        //     cout<<x<<" ";
        // cout<<"\n";
        // cout<<"exa: ";
        // for(auto x : exa)
        //     cout<<x<<" ";
        // cout<<"\n";
        if(need.size()>2)cout<<"NO\n";
        else{
            if(need.size()==0)cout<<"YES\n";
            else if(need.size()==1&&exa.size()==0)cout<<"YES\n";
            else{
                if(need.size()==2&&exa.size()==1){
                    if(exa[0]==need[0]+need[1])cout<<"YES\n";
                    else cout<<"NO\n";
                }else cout<<"NO\n";
            }
        }

        // if(need.size()==2&&(exa[0]==need[0]+need[1]))
        //     cout<<"YES\n";
        // else cout<<"NO\n";
    }   
    return 0;
}

E.Nastya and Potions

题意:炼金术士 Nastya 很喜欢合成药水。现有 $ n $ 种药水,第 $ i $ 种药水可以用 $ c_i $ 个金币买入。

任何一种药水的合成方案都不超过 $ 1 $ 种。在合成某种药水的过程中,作为原料的药水将会被完全消耗。任何药水都不能直接或间接合成它本身。

作为一个经验老道的炼金术士,Nastya 已经可以无限制地获得 $ p_1, p_2, \dots, p_k $ 这 $ k $ 种药水,可是她却没法决定接下来要合成哪些药水。于是,她求助于你。对于 $ 1 \le i \le n $,她需要你求出获得第 $ i $ 种药水所需的最少的金币数。

思路:记忆化搜索+dp

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n,k,c[N];
bool hav[N];
vector<int>e[N];
ll dp[N],ans[N];
ll dfs(int x)
{
    if(hav[x])return 0ll;
    ll &res = dp[x];
    if(res!=-1)return res;
    res = c[x];
    if(e[x].size())
    {
        ll tmp = 0;
        for(auto y : e[x])
            tmp += dfs(y);
        res = min(res,tmp);
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>k;
        for(int i = 1;i <= n; i++)
            cin>>c[i],hav[i] = 0,dp[i] = -1;
        for(int i = 1;i <= k; i++)
        {
            int x; cin>>x;
            hav[x] = true;
        }

        for(int i = 1;i <= n; i++)
        {
            int m; cin>>m;
            e[i].clear();
            for(int j = 1;j <= m; j++)
            {
                int x; cin>>x;
                e[i].push_back(x);
            }
        }
        for(int i = 1;i <= n; i++)
        {
            cout<<dfs(i)<<" ";
        }
        cout<<"\n";
    }
    return 0;
}

F.Lisa and the Martians

题意:给定长度为 \(n\) 的序列 \(a\) 和一个正整数 \(k\),保证 \(0\leq a_i< 2^k\)。求满足 \(0\leq x<2^k\)\(i\neq j\) 的三元组 \((i,j,x)\) 使得 \((a_i\oplus x)\operatorname{and}(a_j\oplus x)\) 最大。如果有多组符合要求的输出任意一组即可。

多组数据。

思路:对于两个数的每一位进行考虑。

设两个数的同一个位置的值分别为\(u,v\)

  • \(u = 1,v = 1\),此时\(x\)这一位取\(0\),使得\(\&\)之后为\(1\)
  • \(u = 0,v = 0\),此时\(x\)这一位取\(1\),使得\(\&\)之后为\(1\)
  • \(u\ne v\)时,无论\(x\)怎么取结果都是\(0\)

为了尽可能达到最大值,高位尽可能相同。我们知道两个数越接近高位的数越相同。那么我们排序之后在相邻两个数之间考虑就行了。注意结果可能是0,maxx初始化为-1。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
array<int,2>a[N];
bool cmp(array<int,2>a,array<int,2>b)
{
	return a[0]>b[0];
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n,k;
		cin>>n>>k;
		for(int i = 1;i <= n; i++)
		{
			cin>>a[i][0];
			a[i][1] = i;
		}
		sort(a+1,a+1+n);
		ll maxx = -1,pos1 = 0,pos2 = 0,tt = 0;
		for(int i = 1;i < n; i++)
		{
			int x = a[i][0],y = a[i+1][0];
			ll w = 0,t = 0;
			for(int j = k-1;j >= 0;j--)
			{
				if(((x>>j)&1)==((y>>j)&1))
				{
					w |= (1<<j);
					if(((x>>j)&1)==0)
						t |= (1<<j);
				}
			}
			if(w>maxx)
				pos1 = a[i][1],pos2 = a[i+1][1],tt = t,maxx = w;
		}
		cout<<pos1<<" "<<pos2<<" "<<tt<<"\n";
	}
	return 0;
}