Codeforces Round #636 (Div. 3)(D线段树 ,E 思维+最短路 F 思维难题)

题目链接

D. Constant Palindrome Sum

题意:给你n长度的数组a[i]  n一定为偶数。现在要你进行一个操作:

选择一个下标i   改变其权值  范围为1~k,问最少的操作使得  所有的  a[i]+a[n-i+1]  都相等

做法:考虑每对a[i] 、a[n-i+1]   一个最小值:mi   一个 最大值:mx

0操作可以得到的范围数:mi+mx

1操作可以得到的范围数:【mi+1,mx+k】

2操作可以得到的范围数: 【1,mi】【mx+k,k】 判断一下mx+k与k 的大小即可

那么线段树区间更新单点查询一下即可,也可以差分一下。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=4e5+10;
int n,k;
int a[N];
ll sum[4*N],lazy[4*N];
void qu(int id,int l,int r,int pos,ll &mx)
{
    mx+=sum[id];
    if(l==r) return ;
    int mid=l+r>>1;
    if(pos<=mid) qu(id<<1,l,mid,pos,mx);
    else qu(id<<1|1,mid+1,r,pos,mx);
}

void up(int id,int l,int r,int ql,int qr,int val)
{
    if(ql<=l&&r<=qr) {
        sum[id]+=val;
        return ;
    }
    int mid=l+r>>1;
    if(ql<=mid) up(id<<1,l,mid,ql,qr,val);
    if(qr>mid) up(id<<1|1,mid+1,r,ql,qr,val);
}

void solve()
{
    scanf("%d%d",&n,&k);
    rep(i,1,n) scanf("%d",&a[i]);

    int len=2*k;
    rep(i,1,4*len) sum[i]=0;

    rep(i,1,n/2) {
       int mi=min(a[i],a[n-i+1]);
       int mx=max(a[i],a[n-i+1]);
       int now=a[i]+a[n-i+1];
       int l=mi+1,r=mx+k;

       up(1,1,len,l,r,1);
       up(1,1,len,now,now,-1);
       up(1,1,len,1,l-1,2);
       if(r+1<=len) up(1,1,len,r+1,len,2);
    }
    ll ans=1e18;
   rep(i,2,len) {
        ll mx=0;qu(1,1,len,i,mx);
        ans=min(ans,mx);
   }
   printf("%lld\n",ans);
}
int main()
{

	int _;cin>>_;while(_--)
	solve();
}

E. Weights Distributing

题意:给你n个节点m条边的无向图,以及未被分配的m个权值w[i]   现在你要从a到b  再从b到c  求如何分配使得,走过的权值和最小,输出这个权值即可。

做法:求每个点到a、b、c的最短路径(假设权值为1)接着枚举中继点a->v  v->b   b->v  v->c 必然会存在这么一个中继点使得权值和最小,那么重复走的路用最小依次分配即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
struct node
{
    int u,w;
    bool operator <(const node&o)const{return o.w<w;}
};
const int N=2e5+10;
vector<int>G[N];
int n,m,a,b,c;
ll w[N];
int d1[N],d2[N],d3[N];
const int inf=1e8;

void bfs(int s,int dis[])
{
    priority_queue<node>que;
    que.push({s,0});
    rep(i,1,n) dis[i]=inf;
    dis[s]=0;
    while(que.size()){
        node now=que.top();que.pop();

        for(auto v:G[now.u]){
            int u1=now.u,v1=v;
            if(dis[v]>dis[now.u]+1){
                dis[v]=dis[now.u]+1;
                que.push({v,dis[v]});
            }
        }
    }
}



void solve()
{

    scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);

    rep(i,1,n){G[i].clear();}

    rep(i,1,m) scanf("%lld",&w[i]);
    rep(i,1,m)
    {
        int u,v;scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }


    bfs(a,d1);
    bfs(b,d2);
    bfs(c,d3);

    sort(w+1,w+1+m);
    rep(i,1,m) w[i]+=w[i-1];

    ll ans=1e18;
    rep(i,1,n){
        int r=d1[i]+d2[i]+d3[i];
        if(r>m) continue;
        ll mx=w[r]+w[d2[i]];
        //printf("r:%d mx:%lld\n",r,mx);
        ans=min(ans,mx);
    }
    printf("%lld\n",ans);
}

int main()
{
	int _;cin>>_;while(_--)
	solve();
}
/*
7
5 5 1 3 5
1 2 2 2 2
1 2
3 4
2 3
2 5
4 5
*/

F. Restore the Permutation by Sorted Segments

这题题解参考来自:博客

题大:现在有一个长度为 n 的排列 p ,但排列 p 暂时对我们保密,每个样例将会给出 n - 1 个排好序后的子段,换句话说,对于 r ∈ [ 2 , n ] ,存在一个 l 满足 l <  r ,题目会给出排列 p 中 [ l , r ] 这段,只不过是排好序后给出,现在让我们还原排列 p 

判断所有子段是否合法想了半天,然后博主的的方法特别不错,用set<set<int> >保存输入的子段然后枚举区间 再查询,只要log复杂度

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int inf=0x3f3f3f3f;
const int N=2e5+100;

int main()
{

	int _;cin>>_;while(_--)
	{
		vector<set<int> >G;
		int n;
		scanf("%d",&n);
		for(int i=1;i<n;i++)
		{
			set<int>st;
			int k;scanf("%d",&k);
			while(k--)
			{
				int num;
				scanf("%d",&num);
				st.insert(num);
			}
			G.push_back(st);
		}




		for(int st=1;st<=n;st++)//枚举第一个数
		{
			vector<int>ans;
			vector<set<int> >cur=G;
			bool flag=true;
			for(auto &it:cur) it.erase(st);//删除第一个数

			ans.push_back(st);
			for(int i=1;i<n;i++)//迭代n-1次,找出剩余n-1个数
			{
				vector<int>temp;
				for(auto &it:cur)
					if(it.size()==1)
						temp.push_back(*it.begin());
				if(temp.size()!=1)//无法找到
				{
					flag=false;
					break;
				}
				ans.push_back(temp[0]);
				for(auto &it:cur) it.erase(temp[0]);
			}
			set<set<int>>all(G.begin(),G.end());
			if(flag)//构造出数列后,判断是否合理
			{
				for(int l=0;l<n-1;l++)
				{
					set<int>st;
					for(int r=l;r<n;r++)
					{
						st.insert(ans[r]);
						if(all.count(st))all.erase(st);
					}
				}
			}
			if(all.empty())//判断合理的话,直接输出答案
			{
				for(int it:ans)printf("%d ",it);
				puts("");
				break;
			}
		}
	}

    return 0;
}
发布了536 篇原创文章 · 获赞 71 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/105673163