[ARC 122]

最近状态差到爆炸.
(AT)连掉两把分,啥时候能上黄啊。

(A)
考虑直接动归。
(O(n^2))的动归后缀和优化成(O(n))

A
#include<iostream>
#include<cstdio>
#define ll long long
#define N 100005
#define mod 1000000007

ll a[N],f[N],g[N],sf[N],sg[N];
ll n,ans,sum;

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i)
	scanf("%lld",&a[i]),sum = (sum + a[i]) % mod;
	//f:i为-时从i到n有多少种合法方案,g:这些合法方案的权数。
	f[n] = 1,g[n] = (-2 * a[n] + mod) % mod;
	f[n + 1] = 1;
	sf[n] = 2,sf[n + 1] = 1,sg[n] = g[n];
	for(int i = n - 1;i >= 2;--i){
		f[i] = sf[i + 2];
		g[i] = (f[i] * (-2 * a[i] + mod) % mod + sg[i + 2]) % mod;
		sf[i] = (sf[i + 1] + f[i]) % mod;
		sg[i] = (sg[i + 1] + g[i]) % mod; 
	}
//	for(int i = n;i >= 2;--i)
//	std::cout<<f[i]<<" "<<g[i]<<std::endl;
	for(int i = n;i >= 2;--i)
	ans = (ans + sum * f[i] % mod + g[i]) % mod;
	ans = (ans + sum) % mod;
	std::cout<<ans<<std::endl;
}

(B)
听说B是一个结论题,正解来看呢,应该是把(n)个可取值都试一遍,但是我写的模拟退火。

B
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#define ll long long
#define N 100005

struct P{int x,y,w;}p[N];

ll n;

double ansx,ansy;

inline  double find(double x){
	double ans = 0;
	for(int i = 1;i <= n;++i)
	ans += (p[i].x + x - std::min((double)p[i].x,(double)(2 * x))) / (1.0 * n);
	return ans;
}

int main(){
//	freopen("q.in","r",stdin);
//	freopen("q.out","w",stdout);
	srand(273352);
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i){
		scanf("%d",&p[i].x);
		ansx += p[i].x;	}
	ansx = (ansx) / (1.0 * n);
	ansy = 0x7f7f7f7f;
	double eps = 1e-15;
	double T = 200;//初始温度 
	while(T > eps && ((double)(clock())/CLOCKS_PER_SEC)<1.9){//终止态 
//		std::cout<<T<<" "<<(rand() * 2 - RAND_MAX) * T<<std::endl;
		double nowx = ansx + ((rand() * 2 - RAND_MAX + 1) * T);//在值域[ansx - t,ansx + t];中挑选一个随机数
		long double z = find(nowx) - find(ansx);
		if(z < 0)
		ansx = nowx,ansy = std::min(ansy,find(nowx));
		else
		if(exp(-z / T) * RAND_MAX > rand())//随机接受 
		ansx = nowx;
		T *= 0.997;//降温速率 
//		std::cout<<ansx<<std::endl;
	}
	printf("%.12lf
",ansy);
}

(C)
考虑每一个数在(fib)数系下都有唯一分解。
做完了。

C
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long

ll f[200],to[200],ti[200];
ll ans[200];

ll n;
ll k;

//0 1
//1 1
//1 2
//1 3
//4 3
//4 7

int main(){
	scanf("%lld",&n);
	ll q = n;
	ll len = 1;
	f[0] = 1,f[1] = 1;
	while(f[len] <= n + 1){
		f[++len] = f[len - 1] + f[len - 2];
	}
	for(int i = len;i >= 1;--i)
	if(n >= f[i])
	to[++to[0]] = i,n -= f[i];
	std::sort(to + 1,to + to[0] + 1);
	for(int i = 1;i <= to[0];++i)
	ti[i] = to[to[0]] - to[i];
	std::sort(ti + 1,ti + to[0] + 1);
	ll s = to[to[0]];
	ll x = 0,y = 0;
	ll now = 1;
	for(int i = 0;i <= s;++i){
		if(i == ti[now]){
			if(i & 1)
			ans[++k] = 1,x += 1;
			else
			ans[++k] = 2,y += 1;
			++now;
		}
		if(i & 1)
		ans[++k] = 4,y += x;
		else
		ans[++k] = 3,x += y;
	}
	std::cout<<k<<std::endl;
	if(x == q){
		for(int i = 1;i <= k;++i)
		std::cout<<ans[i]<<std::endl;
	}
	else{
		for(int i = 1;i <= k;++i){
			if(ans[i] <= 2)
			std::cout<<3 - ans[i]<<std::endl;
			else
			std::cout<<7 - ans[i]<<std::endl;
		}
	}
} 

(D)
考虑如果后手已经想好的每个数对,那么其实游戏进程没有差别。
所以这是一个假博弈。
我们考虑对每一位进行操作,如果这一位的(0),(1)的数量都是偶数,那么递归进子树操作。
否则则把左子树和右子树左右匹配,用(01tire)找出最小的匹配,因为再往下递归的所有对都小于这个匹配。

D
#include<iostream>
#include<cstdio>
#define ll long long

ll to[12000005][2];
ll cnt[12000005];

ll n;

ll dfncnt;

inline void insert(ll x){
	ll u = 0;
	cnt[u] ++ ;
	for(int i = 29;i >= 0;--i){
		int t = (x >> i) & 1;
		if(!to[u][t])
		to[u][t] = ++ dfncnt;
		u = to[u][t];
		cnt[u] ++ ;
	}
} 
 
ll ans = 0,tmp;

#define l(u) to[u][0]
#define r(u) to[u][1]

inline void find(ll p1,ll p2,ll now,ll dep){
//	std::cout<<p1<<" "<<p2<<" "<<now<<" "<<dep<<std::endl;
	if(dep == 0){
		tmp = std::min(now,tmp);
		return;
	}
	bool q = 0;
	for(int i = 0;i <= 1;++i)
	if(to[p1][i] && to[p2][i]){
		q = 1;
		find(to[p1][i],to[p2][i],now,dep - 1);
	}
	if(q)
	return;
	for(int i = 0;i <= 1;++i)
	if(to[p1][i] && to[p2][!i]){
		find(to[p1][i],to[p2][!i],now | (1 << (dep - 1)),dep - 1);
		q = true;
	}
}

inline void dfs(int u,int dep){
//	std::cout<<u<<" "<<dep<<std::endl;
	if(!to[u][1] && !to[u][0])
	return;
	if(cnt[l(u)] % 2 && cnt[r(u)]){
		tmp = (1 << 30);
		find(l(u),r(u),(1 << (dep - 1)),dep - 1);
		ans = std::max(ans,tmp);
		return ;
	}
	if(l(u))
	dfs(l(u),dep - 1);
	if(r(u))
	dfs(r(u),dep - 1);
}

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= 2 * n;++i){
		ll x;
		scanf("%lld",&x);
		insert(x);
	}
	dfs(0,30);
	std::cout<<ans<<std::endl;
}

(E)
由于(lcm(x,y) = frac{x * y}{gcd(x,y)})
考虑最后一个数,那么有(gcd(lcm(a_j),a_i) < a_i)
(lcm(gcd(a_i,a_j)) < a_i)这里是由于精度所以不能选择前一种(
然后依次从后向前选择就好了。

E
#include<iostream>
#include<cstdio>
#define ll long long
#define N 305

ll ans[N],a[N];
bool vis[N];

ll n;

inline ll g(ll a,ll b){return (b == 0) ? a : g(b,a % b);}

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i)
	scanf("%lld",&a[i]);
	for(int i = n;i >= 1;--i){
		bool q;
		for(int j = 1;j <= n;++j){
			ll lcm = 1;
			ll gcd = 1;
			if(!vis[j]){
				q = 1;
				for(int k = 1;k <= n;++k){
					if(!vis[k] && k != j){
						gcd = g(a[k],a[j]);
						lcm = lcm / g(gcd,lcm) * gcd;
						if(lcm >= a[j]){
							q = 0;
							break;
						}
					}
				}
				if(q){
					ans[i] = a[j];
					vis[j] = 1;
					break;
				}
			}
		}
		if(!q){
			puts("No");
			return 0;
		}	
	}
	puts("Yes");
	for(int i = 1;i <= n;++i)
	std::cout<<ans[i]<<" ";
}
原文地址:https://www.cnblogs.com/dixiao/p/14883476.html