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;
 } 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章