Codeforces Round #636 (Div. 3)(A-E)

A k>1,所以系数和s最小等3,k+1,s就*2再+1,直到n取余s等0就可以输出了
(已知解一定存在)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int t,n;
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		int k=3;
		while(n%k)
		{
			k=k<<1|1;
		}
		cout<<n/k<<endl;
	}
	return 0;
 } 

B 构造数组使得前n/2个数是偶数,后n/2个数是奇数,这些数两两不同,而且前n/2个数的和与后n/2个数的和相等(n是偶数)。
n/2个偶数的和一定是偶数,n/2个奇数的和奇偶都有可能,n/2是奇数的话两个和一定不会相等,偶数的话一定可以相等。
n/2是偶数时,令前n/2个数是最小的偶数,后n/2的数是最小的奇数,两部分的和差了n/2,后半部分加上就可以了,n/2是偶数,奇加偶还是奇满足题意。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int t,n;
 
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		if((n/2)&1) puts("NO");
		else 
		{
			puts("YES");
			for(int i=1;i<=n/2;i++) printf("%d ",i*2);
			for(int i=1;i<n/2;i++) printf("%d ",i*2-1);
			printf("%d\n",n-1+n/2); //最后一个数加n/2,后n/2个数都可以加,不过其他地方要特判,像n=4的时候,不能加到第三个数不然就是 2 4 3 3 了,加到最后一个数不用特判
		}
	}
	return 0;
 } 

C 已知一个数组,它的交错子序列是在子序列的基础上再加一个条件:任意相邻的两个元素的符号相反。求长度最长的交错子序列的最大和。
长度要求最长,所以要在每一个连续的符号相同的子段内都选择一个元素加到子序列内,还要求和最大,一个正数加一个负数要想和最大,正数要最大,负数的绝对值要最小,所以负数也要最大,所以每一个元素都要选择他位于的那个符号相同的子段的最大值,直接模拟即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int t,n,a[N];
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		for(int i=0;i<n;i++) cin>>a[i];
		ll k=a[0],res=0;
		for(int i=1;i<n;i++)
		{
			if(k*a[i]<0)
			{
				res+=k;//符号不同了,将前一个子段的最大值加到结果内
				k=a[i];//同时更新最小值
			}
			else  k=max(k,1ll*a[i]);
		}
		cout<<res+k<<endl;//再加最后一个子段的最大值
	}
	return 0;
 } 

D 已知一个数组,数组内的每个数都不能大于k,要将位于对称位置(a[i]和a[n-i+1])的数对的和都相等,求最小需要改变几个数。
每一个数对可以改变0个数,1个数或者两个数,0个数就是他们的和sum,
改变一个数他们的和可以怎么变呢,将他俩中大的那个数变为1,他俩的和就是他俩小的那个数加1,这就是改变一个数它的和的最小值l,将他俩中小的那个数变为k,他俩的和就是大的那个数加k,这就是改变一个数他俩和的最小值r,
在区间[l,r]内除了sum的左右数都是可以改变一个数而得到的;其他区间[2,l-1]和[r+1,2k]内的数都是必须改变两个数才能得到的。
开一个数cnt[ ],cnt[i]就是改变几个数才能是左右数对的和都等i,遍历每个数对,
将他们[2,l-1]和[r+1,2
k]都加2,[l.sum-1]和[sum+1,r]都加一,最后再枚举cnt[]取最小值。
区间加就要用到差分了,直接差分就可以了,我又写了个树状数组对此一举了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int t,n,a[N],k,l[N],r[N];
ll tr[N<<1];
int lowbit(int x)
{
	return x&-x;
}
void add(int x,int d)
{
	for(int i=x;i<=2*k;i+=lowbit(i)) tr[i]+=d;
}
ll sum(int x)
{
	ll res=0;
	for(int i=x;i;i-=lowbit(i)) res+=tr[i];
	return res;
}
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		map<int,int> mp;
		for(int i=1;i<=2*k;i++) tr[i]=0;
		for(int i=1;i<=n;i++) cin>>a[i];
		ll res=0;
		for(int i=1;i<=n/2;i++) 
		{
			l[i]=min(a[i],a[n-i+1])+1;
			r[i]=max(a[i],a[n-i+1])+k;
			int s=a[i]+a[n-i+1];
			add(1,2); add(l[i],-2);
			add(l[i],1); add(s,-1);
			add(s+1,1); add(r[i]+1,-1);
			add(r[i]+1,2);
		}
		res=0x3f3f3f3f;
		for(int i=2;i<=2*k;i++) res=min(res,sum(i));
		cout<<res<<endl;
	}
	return 0;
 } 

E 有n个节点m条路构成一个无向无权图,你计划从点a到点b再到点c,现在政府要对这m条路收费,你知道了这m个费用,让你自己安排问你从a到b再到c的最小费用是多少。
昨天想的是分别跑两边最短路,再记录一下两次都走过了那些边,按边走过的次数次数越多赋越小的权值,结果不行,要想使花费最小就要使两次做过的路径尽可能多的重叠,重叠的边数越多,花费就会越小,跑两边最短路就不行了,因为两次跑图是相互独立的,不能保证重叠的边数多。
题解是枚举一个中间点x,从a出发先到x再到b再回到x再去c这样的顺序来走,这样的话x->b的边就走了两遍,赋上最小的几个边权,计算两次,a->x
和x->c计算一次,枚举n个节点作为x,取最小值就可以了。
所以需要预处理各个节点分别到a,b,c的距离,再计算就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=200010;
int t,n,m,a,b,c,k[N];
ll sum[N];
int h[N*2],e[N*2],ne[N*2],idx;
void add(int a,int b)
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
bool st[N];
int num[N];
vector<int> bfs(int x)
{
	queue<int> q;
	q.push(x);
	vector<int> res(n+1,0x3f3f3f3f);
	res[x]=0;
	bool st[n+10];
	memset(st,0,sizeof st);
	while(q.size())
	{
		int u=q.front();
		q.pop();
		st[u]=1;
		for(int i=h[u];i!=-1;i=ne[i])
		{
			int v=e[i];
			if(!st[v])
			{
				res[v]=min(res[v],res[u]+1);
				q.push(v);
			}
		}
	}
	return res;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
		for(int i=0;i<=n;i++) h[i]=-1;//t<=1e4,memset会超时,处理要用到点就可以了
		for(int i=1;i<=m;i++) scanf("%d",k+i);
		sort(k+1,k+m+1);
		for(int i=1;i<=m;i++) sum[i]=sum[i-1]+k[i];//前缀和
		idx=0;//记得清零
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			add(x,y);
			add(y,x);
		}
		vector<int> dista(n+1),distb(n+1),distc(n+1);//数组长度是n+1
		dista=bfs(a);//处理a到各个节点的距离,下面一样
		distb=bfs(b);
		distc=bfs(c);
		ll res=0x3f3f3f3f3f3f3f3f;
		for(int i=1;i<=n;i++)
		{
			if(dista[i]+distb[i]+distc[i]>m) continue;//如果大于m就不符题意了
			res=min(res,sum[distb[i]]+sum[dista[i]+distc[i]+distb[i]]);
		}
		printf("%lld\n",res);
	}
	return 0;
 } 
posted @ 2020-04-22 15:58  Neflidata  阅读(4)  评论(0编辑  收藏  举报