2018-2019 ICPC Northwestern European Regional Programming Contest (NWERC 2018)题解

题目链接

A. Access Points
题目:

在平面上给定 n n n个点 ( X 1... n , Y 1... n ) (X_{1...n},Y_{1...n}) (X1...n,Y1...n),现在要在平面上确定 n n n个点 ( x 1... n , y 1... n ) (x_{1...n},y_{1...n}) (x1...n,y1...n),满足 ∀ i < j , x i ≤ x j , y i ≤ y j \forall i<j,x_i \le x_j,y_i \le y_j i<j,xixj,yiyj,且 ∑ i = 1 n ( X i − x i ) 2 + ( Y i − y i ) 2 \sum_{i=1}^n(X_i-x_i)^2+(Y_i-y_i)^2 i=1n(Xixi)2+(Yiyi)2最小,输出最小值。
( 1 ≤ n ≤ 1 0 5 ) (1 \le n \le 10^5) (1n105)

题解:

显然横纵坐标独立,所以可以分开考虑,以横坐标为例。如果序列 X X X是非递减的,那么直接令 x i = X i x_i=X_i xi=Xi,答案为0。如果 X i > X i + 1 X_i>X_{i+1} Xi>Xi+1,那么 x i = x i + 1 x_i=x_{i+1} xi=xi+1,所以可以想见序列 x x x是一段一段的。那么考虑某一段,比如 [ i , j ] [i,j] [i,j],设这一段的 x i x_i xi均为 x x x,令 f ( x ) = ∑ k = i j ( X k − x k ) 2 f(x)=\sum_{k=i}^j(X_k-x_k)^2 f(x)=k=ij(Xkxk)2,求导后可知当 x = 1 j − i + 1 ∑ k = i j X k x=\frac{1}{j-i+1}\sum_{k=i}^j X_k x=ji+11k=ijXk时, f ( x ) f(x) f(x)取得最小值,即取它们的平均值。我们假设已经维护出了前缀 [ 1 , i ] [1,i] [1,i] x x x,现在考虑 X i + 1 X_{i+1} Xi+1,要维护出 [ 1 , i + 1 ] [1,i+1] [1,i+1] x x x,如果 x i + 1 x_{i+1} xi+1小于当前最后一段的平均值,那么就把 x i + 1 x_{i+1} xi+1和这一段合并,然后再和前面一段比较平均值往前合并。为什么 x i + 1 x_{i+1} xi+1一定是和前面一段合并,而不会留给后面?因为最优情况下的一段一定满足所有后缀的平均值比对应前缀的平均值要小,不然就可以拆成两段,这样结果会更小,而如果 x i + 1 x_{i+1} xi+1留给后面的话,那么显然不能满足这个条件,所以肯定是和前面的合并的。这个过程很经典,用单调栈维护即可。

复杂度: O ( n ) O(n) O(n)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e6+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,tp;
pii a[maxn];
pair<ll,ll> stk[maxn];
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&a[i].fi,&a[i].se);
	}
	double ans=0;
	//x
	tp=0;
	for(int i=1;i<=n;i++){
		ll sum=a[i].fi,cnt=1;
		while(tp&&stk[tp].fi*cnt>stk[tp].se*sum){
			sum+=stk[tp].fi;
			cnt+=stk[tp].se;
			--tp;
		}
		stk[++tp]=mp(sum,cnt);
	}	
	int p=1;
	for(int i=1;i<=tp;i++){
		double x=1.0*stk[i].fi/stk[i].se;
		for(int j=p;j<=p+stk[i].se-1;j++){
			ans+=(a[j].fi-x)*(a[j].fi-x);
		}
		p+=stk[i].se;
	}

	//y
	tp=0;
	for(int i=1;i<=n;i++){
		ll sum=a[i].se,cnt=1;
		while(tp&&stk[tp].fi*cnt>stk[tp].se*sum){
			sum+=stk[tp].fi;
			cnt+=stk[tp].se;
			--tp;
		}
		stk[++tp]=mp(sum,cnt);
	}	
	p=1;
	for(int i=1;i<=tp;i++){
		double x=1.0*stk[i].fi/stk[i].se;
		for(int j=p;j<=p+stk[i].se-1;j++){
			ans+=(a[j].se-x)*(a[j].se-x);
		}
		p+=stk[i].se;
	}
	printf("%.9f\n",ans);
	return 0;
}
B. Brexit Negotiations
题目:

给定 n n n个会议,第 i i i个会议前有 d i d_i di个前置会议,每个会议关于自身的内容需要 e i e_i ei个时间,且每个会议都要总结之前开过的每一个会议,每个会议1个时间,问持续时间最长的会议的最小值是多少。
( 1 ≤ n ≤ 4 ⋅ 1 0 5 , 1 ≤ e i ≤ 1 0 6 ) (1 \le n \le 4 \cdot 10^5,1 \le e_i \le 10^6) (1n4105,1ei106)

题解:

最优策略是每次取出 e i e_i ei最大的会议,将其前置的会议都开完,然后将它开完。如果将两个非前置关系的会议交换,设一个为 l i + e i l_i+e_i li+ei,另一个为 l j + e j l_j+e_j lj+ej e i > e j , l i < l j e_i>e_j,l_i<l_j ei>ej,li<lj,交换后,变成 x + e j , l j + e i x+e_j,l_j+e_i x+ej,lj+ei,显然 , l j + e i > l i + e i , l j + e i > l j + e j ,l_j+e_i>l_i+e_i,l_j+e_i>l_j+e_j ,lj+ei>li+ei,lj+ei>lj+ej,所以交换后两者的最大值变大,不会更优。

复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=4e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,cnt;
int a[maxn];
vector<int>g[maxn];
int vis[maxn];
struct cmp{
	bool operator()(int p,int q){
		return a[p]<a[q];
	}
};
priority_queue<int,vector<int>,cmp>pq;
void dfs(int u){
	if(vis[u])return;
	++cnt;
	vis[u]=1;
	for(auto v:g[u]){
		dfs(v);
	}
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&n);
	int x,y;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		scanf("%d",&x);
		for(int j=1;j<=x;j++){
			scanf("%d",&y);
			g[i].pb(y);
		}
		pq.push(i);
	}
	cnt=0;
	int ans=0;
	while(!pq.empty()){
		int u=pq.top();
		pq.pop();
		dfs(u);
		ans=max(ans,a[u]+cnt-1);
	}
	printf("%d\n",ans);
	return 0;
}
C. Circuit Board Design
题目:

给定一棵 n n n个结点的树,现在在平面上给每个点确定一个坐标,使点与点之间不能靠的太近,边是直的且不能相交,长度为1。
( 1 ≤ n ≤ 1000 ) (1 \le n \le 1000) (1n1000)

题解:

选定一个结点放在 ( 0 , 0 ) (0,0) (0,0),第1条边的向量为 ( 1 , 0 ) (1,0) (1,0),然后进行 d f s dfs dfs,每条边比上一条确定的边多逆时针旋转 π n \frac{\pi}{n} nπ度,形成一棵向上长又向左偏的树。显然这样的构造可以满足题意。

复杂度: O ( n ) O(n) O(n)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-8;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1005;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
//符号函数 
int sgn(double x){
	if(fabs(x)<eps)return 0;
	else if(x>0)return 1;
	else return -1;
}
//浮点数比较 
int dcmp(double x,double y){
	if(fabs(x-y)<eps)return 0;
	else if(x>y)return 1;
	else return -1;
} 

//点 
struct Point{
	double x,y;
	Point(double x=0,double y=0):x(x),y(y){}
};
//向量
typedef Point Vector;
//向量加法 
Vector operator+(Vector a,Vector b){
	return Vector(a.x+b.x,a.y+b.y);
}
//向量减法 
Vector operator-(Vector a,Vector b){
	return Vector(a.x-b.x,a.y-b.y);
}
//向量数乘 
Vector operator*(Vector a,double p){
	return Vector(a.x*p,a.y*p);
}
Vector operator/(Vector a,double p){
	return Vector(a.x/p,a.y/p);
}
//两个点判等 
bool operator==(const Point &a,const Point &b){
	if(dcmp(a.x,b.x)==0&&dcmp(a.y,b.y)==0)return true;
	else return false;
}
//点积 
double Dot(Vector a,Vector b){
	return a.x*b.x+a.y*b.y;
}
//叉积 
double Cross(Vector a,Vector b){
	return a.x*b.y-a.y*b.x;
}
//模长 
double Length(Vector a){
	return sqrt(Dot(a,a));
}
//向量逆时针旋转后的向量,逆时针为正 
Vector Rotate(Vector a,double rad){
	return Vector(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));
} 
int n;
vector<int>g[maxn];
Point p[maxn];
Vector vec;
double rad;
void dfs(int u,int fa){
	for(auto v:g[u]){
		if(v==fa)continue;
		p[v]=p[u]+vec;
		vec=Rotate(vec,rad);
		dfs(v,u);
	}
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&n);
	int u,v;
	for(int i=1;i<=n-1;i++){
		scanf("%d%d",&u,&v);
		g[u].pb(v);
		g[v].pb(u);
	}
	vec=Vector(1,0);
	rad=pi/1010;
	p[u]=Point(0,0);
	dfs(1,0);
	for(int i=1;i<=n;i++){
		printf("%.7f %.7f\n",p[i].x,p[i].y);
	}
	return 0;
}
D. Date Pickup

E. Equality Control
题目:

咕,看原题题面

题解:

照着模拟即可。将所有的数取出来,构成数组 a , b a,b a,b,处理出极大 s o r t sort sort区间和极大 s h u f f l e shuffle shuffle区间,显然这些区间不会有交集,将区间内的元素都相同的极大 s h u f f l e shuffle shuffle区间给删掉,因为这等于没 s h u f f l e shuffle shuffle。将所有这些区间 s o r t sort sort一下,最后比较 a , b a,b a,b是否相等,两个极大 s h u f f l e shuffle shuffle区间是否相等即可。

复杂度: O ( l e n A + l e n B ) O(len_A+len_B) O(lenA+lenB)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e6+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n1,n2;
char s1[maxn],s2[maxn];
int a[maxn],b[maxn],v1[maxn],v2[maxn];
vector<pii>sh[2],so[2],ve[2];
int solve(char *s,int *a,int *v,vector<pii>&ve){
	int len=strlen(s+1);
	int n=0;
	vector<pii>so,sh;
	for(int i=1,j;i<=len;i=j){
		if(s[i]=='s'){
			if(s[i+1]=='h'){
				int num=0,f=0,tmp=0,st=n,ed;
				for(j=i+7;j<=len;j++){
					if(s[j]=='(')++num;
					else if(s[j]==')')--num;
					if('0'<=s[j]&&s[j]<='9'){
						tmp=tmp*10+s[j]-'0';
						if(!f)f=1;
					}
					else{
						if(f){
							f=0;
							a[++n]=tmp;
							tmp=0;
						}
					}
					if(!num)break;
				}
				ed=n;
				sh.pb(mp(st+1,ed));
			}
			else{
				int num=0,f=0,tmp=0,st=n,ed;
				for(j=i+6;j<=len;j++){
					if(s[j]=='(')++num;
					else if(s[j]==')')--num;
					if('0'<=s[j]&&s[j]<='9'){
						tmp=tmp*10+s[j]-'0';
						if(!f)f=1;
					}
					else{
						if(f){
							f=0;
							a[++n]=tmp;
							tmp=0;
						}
					}
					if(!num)break;
				}
				ed=n;
				so.pb(mp(st+1,ed));
			}
			j++;
		}
		else if(s[i]=='['){
			int f=0,tmp=0;
			for(j=i;j<=len;j++){
				if('0'<=s[j]&&s[j]<='9'){
					tmp=tmp*10+s[j]-'0';
					if(!f)f=1;
				}
				else{
					if(f){
						f=0;
						a[++n]=tmp;
						tmp=0;
					}
				}
				if(s[j]==']')break;
			}
			j++;
		}
		else{
			j=i+1;
		}
	}
	for(int i=0;i<sz(so);i++){
		sort(a+so[i].fi,a+so[i].se+1);
	}
	for(int i=0;i<sz(sh);i++){
		int flag=1;
		for(int j=sh[i].fi+1;j<=sh[i].se;j++){
			if(a[j]!=a[sh[i].fi]){
				flag=0;
				break;
			}
		}
		if(flag)continue;
		sort(a+sh[i].fi,a+sh[i].se+1);
		ve.pb(sh[i]);
	}
	return n;
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%s%s",s1+1,s2+1);
	n1=solve(s1,a,v1,ve[0]);
	n2=solve(s2,b,v2,ve[1]);
	if(n1!=n2){
		puts("not equal");
		return 0;
	}
	int flag=1;
	for(int i=1;i<=n1;i++){
		if(a[i]!=b[i]){
			flag=0;
			break;
		}
	}
	if(!flag){
		puts("not equal");
		return 0;
	}
	if(ve[0]!=ve[1]){
		puts("not equal");
		return 0;
	}
	puts("equal");
	return 0;
}
F. Fastest Speedrun

暂咕

G. Game Design
题目:

咕,看原题题面

题解:

分析一下问题后可知,这道题在构造过程中有两个限制条件,一个是过程中不会回到原点,另一个是对于每一个指令都需要至少走一步。当最后三个指令为形如"RLR"这样的时候就无解。显然我们需要逆着从终点(原点)开始构造,在过程中调整步长,初始步长为 1 0 8 10^8 108。如果当前指令和前一个指令、后一个指令都是逆指令关系,如当前位"R",前一个和后一个都是"L",这时步长不变,否则将步长减半,然后再用这个步长去更新当前位置。由于每次步长更新的时候会减半,所以从0走出一步以后就不会在回到0了,可以满足限制。
总觉得这道题应该有很多乱搞的玄学方法,因为数据不可能测出所有的情况

复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=25;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
char s[maxn];
vector<pii>p;
set<pii>ans;
char rev[505];
int main(void){
	// freopen("in.txt","r",stdin);	
	rev['U']='D';rev['D']='U';rev['L']='R';rev['R']='L';
	scanf("%s",s+1);
	int n=strlen(s+1);
	if(n>=3&&rev[s[n]]==s[n-1]&&s[n]==s[n-2]){
		puts("impossible");
		return 0;
	}
	pii cur=mp(0,0);
	int step=100000000;
	for(int i=n;i>=1;i--){
		if(i==n||(s[i+1]==rev[s[i]]&&s[i+1]==s[i-1]));
		else step/=2;
		if(i!=n){
			if(s[i]=='L'){
				ans.insert(mp(cur.fi-1,cur.se));
			}
			else if(s[i]=='R'){
				ans.insert(mp(cur.fi+1,cur.se));
			}
			else if(s[i]=='U'){
				ans.insert(mp(cur.fi,cur.se+1));
			}
			else{
				ans.insert(mp(cur.fi,cur.se-1));
			}
		}
		if(s[i]=='L'){
			cur.fi+=step;
		}
		else if(s[i]=='R'){
			cur.fi-=step;
		}
		else if(s[i]=='U'){
			cur.se-=step;
		}
		else{
			cur.se+=step;
		} 
	}
	printf("%d %d\n",cur.fi,cur.se);
	printf("%d\n",sz(ans));
	for(auto v:ans){
		printf("%d %d\n",v.fi,v.se);
	}
	return 0;
}
H. Hard Drive
题目:

给定 n , c , b n,c,b n,c,b和一个长度为 b b b的序列 x 1... b x_{1...b} x1...b x x x必定包含 n n n,必定不包含1),要你构造一个长度为 n n n的01序列 a a a,满足对于 ∀ i ∈ [ 1 , b ] , a x i = 0 \forall i \in [1,b], a_{x_i}=0 i[1,b],axi=0且满足 j = i + 1 , a i ≠ a j j=i+1,a_i \ne a_j j=i+1,ai=aj ( i , j ) (i,j) (i,j)有恰好 c c c个。(数据保证至少有一种构造方案)
( 1 ≤ n ≤ 5 ⋅ 1 0 5 , 1 ≤ c , b ≤ n − 1 ) (1 \le n \le 5 \cdot 10^5,1 \le c,b\le n-1) (1n5105,1c,bn1)

题解:

题目中有特殊条件“ x x x必定包含 n n n,必定不包含1”。如果 c c c为奇,我们令 a 1 = 1 a_1=1 a1=1,为偶,令 a 1 = 0 a_1=0 a1=0,然后隔开来填 ⌊ c 2 ⌋ \displaystyle \lfloor\frac{c}{2} \rfloor 2c个1,其他位置的填0。

复杂度: O ( n ) O(n) O(n)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=5e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,c,b;
int a[maxn];
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d%d%d",&n,&c,&b);
	for(int i=1;i<=n;i++)a[i]=-1;
	int x;
	for(int i=1;i<=b;i++){
		scanf("%d",&x);
		a[x]=0;
	}
	int tp=2;
	if(c%2==1){
		a[1]=1;
		tp++;
		c--;
	}
	for(int i=1;i<=c/2;i++){
		while(a[tp]!=-1){
			tp++;
		}
		a[tp]=1;
		tp+=2;
	}
	for(int i=1;i<=n;i++){
		if(a[i]==-1)a[i]=0;
	}
	for(int i=1;i<=n;i++){
		printf("%d",a[i]);
	}
	puts("");
	return 0;
}
I. Inflation
题目:

给定一个长度为 n n n c c c,要你重新排序,使 c i ≤ i c_i \le i cii,且要最大化 min ⁡ 1 ≤ i ≤ n { c i i } \displaystyle \min \limits_{1 \le i \le n}\{\frac{c_i}{i}\} 1inmin{ici},输出最大化的 min ⁡ 1 ≤ i ≤ n { c i i } \displaystyle \min \limits_{1 \le i \le n}\{\frac{c_i}{i}\} 1inmin{ici}或报告不存在这样的顺序。
( 1 ≤ n ≤ 1 0 5 , 0 ≤ c i ≤ n ) (1 \le n \le 10^5,0 \le c_i \le n) (1n105,0cin)

题解:

最优的顺序就是将 c c c从小到大排,因为将其中任意两个交换顺序,结果不会更优。

复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=2e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n;
int a[maxn];
int main(void){
	//freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	sort(a+1,a+n+1);
	int f=1;
	double ans=1e18;
	for(int i=1;i<=n;i++){
		if(a[i]>i){
			f=0;
			break;
		}
		ans=min(ans,1.0*a[i]/i);
	}
	if(f){
		printf("%.6f\n",ans);
	}
	else{
		puts("-1");
	}
	return 0;
}
J. Jinxed Betting
题目:

咕,看原题题面

题解:

首先要构造出最坏的策略,显然 p 1 p_1 p1在这个过程中始终保持不变,每次要使尽量多的人的分数增加,设当前除 p 1 p_1 p1以外的最大值的个数为 n u m num num,那么每次最多可以让 n − 1 − n u m + ⌊ n u m 2 ⌋ n-1-num+\lfloor \frac{num}{2} \rfloor n1num+2num个人的分数增加。但是我们如果按照分数去模拟的话,复杂度太大,而我们发现每次经过 ⌊ l o g 2 n u m ⌋ + 1 \lfloor log_2num \rfloor+1 log2num+1轮后,最大值和次大值之间的差就会少1,所以经过 d i f ⋅ ( ⌊ l o g 2 n u m ⌋ + 1 ) dif \cdot (\lfloor log_2num \rfloor+1) dif(log2num+1)轮后,最大值和次大值就会合并,其中 d i f dif dif为最大值和次大值之差,这个过程中最大值变大了 d i f ⋅ ⌊ l o g 2 n u m ⌋ dif \cdot \lfloor log_2num \rfloor diflog2num。所以我们模拟这个合并的过程即可。(细节较多)

复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n;
ll a[maxn];
pair<ll,int>b[maxn];
int cmp(ll a,ll b){
	return a>b;
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	sort(a+2,a+n+1,cmp);
	int p=0;
	for(int i=2,j;i<=n;i=j){
		for(j=i;j<=n;j++){
			if(a[j]==a[i])continue;
			break;
		}
		b[++p]=mp(a[i],j-i);
	}
	ll top=b[1].fi,add=0;
	int cnt=b[1].se,f=0;
	for(int i=2;i<=p;i++){
		int tmp=cnt,k=0;
		while(tmp>=2){
			tmp/=2;
			++k;
		}
		ll dif=top-(b[i].fi+add);
		ll cc=dif*(k+1),dd=dif*k;
		if(top+dd>a[1]){
			ll tt=a[1]-top;
			ll num=tt/k;
			add+=num*(k+1);
			top+=num*k;
			tt=a[1]-top;
			add+=tt+1;
			f=1;
			break;
		}
		else{
			add+=cc;
			cnt+=b[i].se;
			top+=dd;
		}
	}
	if(!f){
		int tmp=cnt,k=0;
		while(tmp>=2){
			tmp/=2;
			++k;
		}
		ll tt=a[1]-top;
		ll num=tt/k;
		add+=num*(k+1);
		top+=num*k;
		tt=a[1]-top;
		add+=tt+1;
	}
	printf("%lld\n",add-1);
	return 0;
}
K. Kleptography
题目:

给定一个长度为 m m m的密文 b b b和一个长度为 m m m的明文的最后 n n n个字符,给定加密规则:秘钥 k k k满足 k n + i = a i , i ≥ 1 k_{n+i}=a_i,i \ge 1 kn+i=ai,i1 b i = a i + k i m o d    26 b_i=a_i+k_i \mod 26 bi=ai+kimod26。求明文 a a a
( 1 ≤ n ≤ 30 , n − 1 ≤ m ≤ 100 ) (1 \le n \le 30,n-1 \le m \le 100) (1n30,n1m100)

题解:

从后往前处理,可以保证当前的 a i a_i ai已知,然后求出 k i k_i ki,当 i > n i >n i>n时,令 a i − n = k i a_{i-n}=k_i ain=ki

复杂度: O ( n ) O(n) O(n)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=26;
const int INF=0x3f3f3f3f;
const int maxn=205;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,m;
char k[maxn],b[maxn],a[maxn];
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	scanf("%s%s",a+m-n+1,b+1);
	for(int i=m-n+1;i<=m;i++)a[i]-='a';
	for(int i=1;i<=m;i++)b[i]-='a';
	for(int i=m;i>=1;i--){
		k[i]=(b[i]-a[i]+mod)%mod;
		if(i>n){
			a[i-n]=k[i];
		}
	}
	for(int i=1;i<=m;i++){
		printf("%c",'a'+a[i]);
	}
	puts("");
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值