2017 CCPC Qinhuangdao Site

A. Balloon Robot

假设机器人$0$时刻位于$0$号位置,那么每个气球所需的时间为$(s_a-b)\bmod m$。

将所有气球按这个时间排序,枚举每个气球的时间作为偏移量,得出最优解即可。

时间复杂度$O(p\log p)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1000010;
int T,n,m,p,i,s[N];ll f[N],init,ans;
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d",&n,&m,&p);
		for(i=1;i<=n;i++)scanf("%d",&s[i]);
		init=0;
		for(i=1;i<=p;i++){
			int a,b;
			scanf("%d%d",&a,&b);
			f[i]=((s[a]-b)%m+m)%m;
			init+=f[i];
		}
		ans=init;
		sort(f+1,f+p+1);
		for(i=1;i<=p;i++)ans=min(ans,init-1LL*p*f[i]+1LL*(i-1)*m);
		printf("%lld\n",ans);
	}
}

  

B. Expected Waiting Time

设$f_n$表示长度为$n$的合法括号序列个数,则奇数为$0$,偶数为卡特兰数。

分母一定是$f_{2n}$,而对于分子,可以考虑每个位置作为左右括号的贡献,假设是第$i$个位置作为左括号,那么枚举与其配对的右括号的距离$j$,则中间的方案数为$f_{j-1}$,剩下部分的方案数为$f_{2n-j-1}$,故总方案数为$\sum_{j=1}^{2n-i}f_{j-1}f_{2n-j-1}$。

注意到$i$只影响上式的求和上界,故直接计算出每个的值即可。

时间复杂度$O(n)$。

#include<cstdio>
typedef long long ll;
const int N=5000000;
int n,P,b,A,B,T,i,a[N],sum,ans;
int h[N],inv[N];
inline ll po(ll a,ll b){
	ll t=1;
	for(;b;b>>=1,a=a*a%P)if(b&1)t=t*a%P;
	return t;
}
inline int C(int n){
	if(n&1)return 0;
	return h[n>>1];
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d%d%d",&n,&P,&b,&A,&B);
		h[0]=1;
		h[1]=1;
		inv[0]=inv[1]=1;
		for(i=2;i<=n+1;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P;
		for(i=2;i<=n;i++)h[i]=1LL*h[i-1]*(i*4-2)%P*inv[i+1]%P;
		n*=2;
		for(i=1;i<=n;i++){
			b=(1LL*b*A+B)%P;
			a[i]=(1LL*a[i-1]+1LL*b+1)%P;
		}
		sum=0;
		ans=0;
		for(i=1;i<n;i++){
			//n-x = i
			//x=n-i
			sum=(1LL*C(i-1)*C(n-i-1)+sum)%P;
			ans=(1LL*(P-a[n-i])*sum+ans)%P;
			ans=(1LL*a[i+1]*sum+ans)%P;
		}
		printf("%d\n",1LL*ans*po(C(n),P-2)%P);
	}
}

  

C. Crusaders Quest

爆搜所有可行方案即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
char w[4] = "gao";
int p[4];
int ANS;
void dfs(int o, string s, int val)
{
	if(o == 2)
	{
		gmax(ANS, val + 1);
		return;
	}
	int len = s.length();
	
	string nxt = "";
	for(int j = 0; j < len; ++j)if(s[j] != w[p[o]])nxt = nxt + s[j];
	
	bool flag = 0;
	for(int i = 0; i < len; ++i)if(s[i] == w[p[o]])
	{
		flag = (s[i] == s[i + 1] && s[i] == s[i + 2]);
		break;
	}
	
	dfs(o + 1, nxt, val + flag);
}
int main()
{
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		string s;
		cin >> s;
		for(int i = 0; i < 3; ++i)p[i] = i;
		ANS = 0;
		do
		{
			dfs(0, s, 0);
		}while(next_permutation(p, p + 3));
		printf("%d\n", ANS);
	}
	return 0;
}
/*
【trick&&吐槽】


【题意】


【分析】


【时间复杂度&&优化】


*/

  

D. Graph Generator

从后往前还原整个过程。

若图只有一个点,那么方案显然。

若图由多个连通块构成,那么每个连通块之间是独立的,分开处理即可。

否则图只剩下一个大小至少为$2$的连通块,那么最后一步操作必然选择一个到其它每个点都有边的点,然后将图继续分裂成若干连通块。

注意到对于$n$个点的连通块,边数会减少$n-1$,故迭代层数不超过$O(\sqrt{n})$。

时间复杂度$O(n\sqrt{n})$。

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int>P;
const int N=100010,M=200010;
int Case,n,m,i,qa[N],qb[M],tmp[M];
int fin[N];//should reverse
vector<int>ans[N];
int stage;
bool err;
struct E{
	int x,y;
}e[M];
int deg[N],g[N],v[M<<1],nxt[M<<1],ed;
int vis[N];
int cnt;
int q[N],cq;
int pos[M];
inline void add(int x,int y){
	v[++ed]=y;
	nxt[ed]=g[x];
	g[x]=ed;
	deg[x]++;
}
void dfs(int x){
	if(vis[x])return;
	vis[x]=cnt;
	q[++cq]=x;
	for(int i=g[x];i;i=nxt[i])dfs(v[i]);
}
void solve(int nl,int nr,int ml,int mr,vector<int>&old){
	if(err)return;
	//for(int i=nl;i<=nr;i++)printf("%d ",qa[i]);puts("");
	if(nl==nr){
		fin[++stage]=qa[nl];
		old.push_back(qa[nl]);
		return;
	}
	//find connect comp
	int i,j;
	cnt=ed=0;
	for(i=nl;i<=nr;i++){
		int x=qa[i];
		deg[x]=g[x]=vis[x]=0;
	}
	for(i=ml;i<=mr;i++)add(e[qb[i]].x,e[qb[i]].y),add(e[qb[i]].y,e[qb[i]].x);
	cq=0;
	for(i=nl;i<=nr;i++){
		int x=qa[i];
		if(!vis[x]){
			old.push_back(x);
			cnt++;
			dfs(x);
		}
	}
	if(cnt==1){
		int cv=nr-nl;
		for(i=nl;i<=nr;i++)if(deg[qa[i]]==cv)break;
		if(i>nr){
			err=1;
			return;
		}
		int x=qa[i];
		swap(qa[nl],qa[i]);
		int L=ml,R=ml-1;
		for(i=ml;i<=mr;i++){
			int u=e[qb[i]].x,v=e[qb[i]].y;
			if(u==x||v==x)continue;
			tmp[++R]=qb[i];
		}
		for(i=L;i<=R;i++)qb[i]=tmp[i];
		fin[++stage]=x;
		solve(nl+1,nr,L,R,ans[stage]);
	}else{
		vector<P>nson,mson;
		int l=nl;
		for(i=1;i<=cq;i=j){
			int r=l-1;
			for(j=i;j<=cq&&vis[q[i]]==vis[q[j]];j++){
				qa[++r]=q[j];
			}
			nson.push_back(P(l,r));
			l=r+1;
		}
		for(i=1;i<=cnt;i++)pos[i]=0;
		pos[0]=ml-1;
		for(i=ml;i<=mr;i++)pos[vis[e[qb[i]].x]]++;
		for(i=1;i<=cnt;i++)pos[i]+=pos[i-1];
		for(i=1;i<=cnt;i++)mson.push_back(P(pos[i-1]+1,pos[i]));
		for(i=ml;i<=mr;i++){
			int x=vis[e[qb[i]].x];
			tmp[pos[x]--]=qb[i];
		}
		for(i=ml;i<=mr;i++)qb[i]=tmp[i];
		int _cnt=cnt;
		for(i=0;i<_cnt;i++){
			ans[0].clear();
			solve(nson[i].first,nson[i].second,mson[i].first,mson[i].second,ans[0]);
		}
	}
}
int main(){
	scanf("%d",&Case);
	while(Case--){
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)qa[i]=i;
		for(i=1;i<=m;i++)qb[i]=i;
		for(i=1;i<=m;i++)scanf("%d%d",&e[i].x,&e[i].y);
		stage=err=0;
		for(i=0;i<=n;i++)ans[i].clear();
		solve(1,n,1,m,ans[0]);
		if(err)puts("No");else{
			puts("Yes");
			for(i=n;i;i--){
				printf("%d %d",fin[i],ans[i].size());
				for(int j=0;j<ans[i].size();j++)printf(" %d",ans[i][j]);
				puts("");
			}
		}
	}
}

  

E. String of CCPC

设$f[i][j][k]$表示考虑前$i$个字符,目前购买了$j$次,与“CCPC”KMP的指针为$k$的最大净收益。

注意到最优解中$j$为个位数,令其不超过$9$即可。

时间复杂度$O(n)$。

#include<cstdio>
const int N=200010,M=10;
int T,n,m,i,j,k,t,ans,b[N],nxt[N],g[M][2],w[M][2];
int f[N][M][4];
char a[N];
inline void up(int&a,int b){a<b?(a=b):0;}
int main(){
	m=4;
	b[1]=0;
	b[2]=0;
	b[3]=1;
	b[4]=0;
	for(i=2;i<=m;nxt[i++]=j){
		while(j&&b[j+1]!=b[i])j=nxt[j];
		if(b[j+1]==b[i])j++;
	}
	for(i=0;i<m;i++){
		for(j=0;j<2;j++){
			int k=i,o=0;
			while(k&&b[k+1]!=j)k=nxt[k];
			if(b[k+1]==j)k++;
			if(k==m)k=nxt[k],o++;
			g[i][j]=k;
			w[i][j]=o;
		}
	}
	scanf("%d",&T);
	while(T--){
		scanf("%d%s",&n,a+1);
		for(i=1;i<=n;i++)a[i]=a[i]=='P';
		for(i=0;i<=n;i++)for(j=0;j<M;j++)for(k=0;k<m;k++)f[i][j][k]=-10000000;
		f[0][0][0]=0;
		for(i=0;i<=n;i++)for(j=0;j<M;j++)for(k=0;k<m;k++){
			if(j+1<M){
				for(t=0;t<2;t++)up(f[i][j+1][g[k][t]],f[i][j][k]+w[k][t]-j);
			}
			if(i<n)up(f[i+1][j][g[k][a[i+1]]],f[i][j][k]+w[k][a[i+1]]);
		}
		ans=0;
		for(j=0;j<M;j++)for(k=0;k<m;k++)up(ans,f[n][j][k]);
		printf("%d\n",ans);
	}
}

  

F. Getting Lost

留坑。

 

G. Numbers

在二进制下从高位到低位贪心考虑。

若答案这一位可以为$0$,那么全部填$0$,否则尽量填$1$。

import java.util.*;

import javax.swing.text.TabableView;

import java.io.*;
import java.math.*;

public class Main
{
	static final int N = (int)1e5 + 10;
	static Scanner cin = new Scanner(System.in);
	static BigInteger v0 = BigInteger.valueOf(0);
	static BigInteger v1 = BigInteger.valueOf(1);
	static BigInteger v2 = BigInteger.valueOf(2);
	static BigInteger b[] = new BigInteger [4000];
	public static void main(String args[])
	{
		b[0] = v1;
		for(int i = 1; i < 4000; ++i)b[i] = b[i - 1].multiply(v2);
		int casenum = cin.nextInt();
		for(int casei = 1; casei <= casenum; ++casei)
		{
			BigInteger n = cin.nextBigInteger();
			BigInteger m = cin.nextBigInteger();
			BigInteger top = n.add(m).subtract(v1).divide(m);
			int w = 0;
			while(b[w].compareTo(top) <= 0)++w;
			BigInteger ans = v0;
			for(int k = w; k >= 0; --k)
			{
				if(n.compareTo( b[k].subtract(v1).multiply(m) ) <= 0)
				{
					
				}
				else
				{
					BigInteger g = n.divide(b[k]);
					if(g.compareTo(m) > 0)g = m;
					n = n.subtract(g.multiply(b[k]));
					ans = ans.add(b[k]);
				}
			}
			System.out.println(ans);
		}
	}
}

  

H. Prime Set

按奇偶建立二分图匹配模型,先忽略$1$求出最大匹配,再考虑$1$继续求最大匹配,每个匹配都将贡献$2$。

对于剩下的数,再检查能否与已选的数配对,每次配对贡献$1$。

注意特殊处理$1$内部的匹配。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 3030, M = N * N, Z = 1e9 + 7, inf = 0x3f3f3f3f;
//edge_num = ???
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
bool is_prime[(int)2e6 + 10];
int nn, K;
int o[N], e[N];

int ST, ED, pone;
int first[N], id;
int w[M], cap[M], nxt[M];
void ins(int x, int y, int cap_)
{
	w[++id] = y;
	cap[id] = cap_;
	nxt[id] = first[x];
	first[x] = id;
	w[++ id] = x;
	cap[id] = 0;
	nxt[id] = first[y];
	first[y] = id;
}

int d[N];
bool bfs()
{
	//MS(d, -1);
	for(int i = 0; i <= ED; ++i)d[i] = -1;
	queue<int> q; q.push(ST); d[ST] = 0;
	while(! q.empty()){
		int x = q.front(); q.pop();
		for(int z = first[x]; z; z = nxt[z]) if(cap[z]){
			int y = w[z];
			if(d[y] == -1){
				d[y] = d[x] + 1;
				q.push(y);
				if(y == ED) return 1;
			}
		}
	}
	return 0;
}
int dfs(int x, int all)
{
	if(x == ED) return all;
	int use = 0;
	for(int z = first[x]; z; z = nxt[z] ) if(cap[z]){
		int y = w[z];
		if(d[y] == d[x] + 1){
			int tmp = dfs(y, min(cap[z], all - use));
			cap[z] -= tmp;
			cap[z ^ 1] += tmp;
			use += tmp;
			if(use == all) break;
		}
	}
	if(use == 0) d[x] = -1;
	return use;
}

int dinic()
{
	int ret = 0;
	while(bfs()) ret += dfs(ST, inf);
	return ret;
}

void prime_init()
{
	int top = 2e6;
	MS(is_prime, 1); is_prime[0] = is_prime[1] = 0;
	for(int i = 2; i <= top; ++i)if(is_prime[i])
	{
		for(int j = i + i; j <= top; j += i)
		{
			is_prime[j] = 0;
		}
	}
}

int main()
{
	prime_init();
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d", &nn, &K);
		int odd = 0;
		int even = 0;
		int one = 0;
		for(int i = 1; i <= nn; ++i)
		{
			int x; scanf("%d", &x);
			if(x == 1)++one;
			else if(x & 1)o[++odd] = x;
			else e[++even] = x;
		}o[0] = 1;
		pone = 0; ST = odd + even + 1; ED = ST + 1;
		for(int i = 0; i <= ED; ++i)first[i] = 0; id = 1;
		for(int i = 0; i <= odd; ++i)
		{
			for(int j = 1; j <= even; ++j)if(is_prime[o[i] + e[j]])
			{
				ins(i, odd + j, 1);
			}
		}
		for(int i = 1; i <= odd; ++i)ins(ST, i, 1);
		for(int i = 1; i <= even; ++i)ins(odd + i, ED, 1);
		int ORI_ONE = one;
		
		int now = dinic();
		//
		//printf("two-two pair = %d\n", now);
		//
		while(one)
		{
			ins(ST, pone, 1);
			if(dinic())
			{
				++now;
				--one;
			}
			else break;
		}
		now += one / 2;
		one %= 2;
		if(K <= now)
		{
			printf("%d\n", K * 2);
			continue;
		}
		//K > now
		K -= now;
		
		int sum = 0;
		for(int i = (1 - one); i <= odd; ++i)if(i == 0 || cap[first[i]] == 0)////
		{
			bool flag = 0;
			for(int j = 1; j <= even; ++j)if(is_prime[o[i] + e[j]])
			{
			
				//
				//printf("single odd for even = %d %d\n", o[i], e[j]);
				//
				
				sum += 1;
				flag = 1;
				break;
			}
			if(!flag && i == 0 && ORI_ONE > 1)sum += 1;
		}
		
		//
		//for(int i = 1; i <= even; ++i)printf("cap[%d] = %d\n", e[i], cap[first[odd + i]]);
		//
		
		for(int i = 1; i <= even; ++i)if(cap[first[odd + i]] == 1)////
		{
			int st = ORI_ONE ? 0 : 1;
			for(int j = st; j <= odd; ++j)if(is_prime[e[i] + o[j]])
			{
			
				//
				//printf("single even for one = %d %d\n", e[i], o[j]);
				//
				
				sum += 1;
				break;
			}
		}
		int ans = now * 2 + min(sum, K);
		printf("%d\n", ans);
	}
	return 0;
}
/*
【trick&&吐槽】


【题意】


【分析】


【时间复杂度&&优化】
6 2
1 1 1 1 2 2
6 3
1 1 1 1 2 2

6 2
1 1 10 10 10 10

7 2
1 1 1 1 1 2 2

7 3
1 1 1 1 1 2 2

7 4
1 1 1 1 1 2 2


*/

  

I. Triangulation

留坑。

 

J. Tree Equation

留坑。

 

K. Diversity and Variance

留坑。

 

L. One-Dimensional Maze

答案为$\min([2,m]中R的个数,[m,n-1]中L的个数)$。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1000010;
int T,n,m,i,A,B;char a[N];
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%s",&n,&m,a+1);
		A=B=0;
		for(i=2;i<=m;i++)if(a[i]=='R')A++;
		for(i=m;i<n;i++)if(a[i]=='L')B++;
		printf("%d\n",min(A,B));
	}
}

  

M. Safest Buildings

下一轮安全区的圆心可行范围可以用圆表示,圆交求出概率即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
const double eps = 1e-8, PI = acos(-1.0);

struct circle
{
	long double x, y, r;
}p1, p2;
long double sqr(long double x)
{
	return x * x;
}
long double dis(circle a, circle b)
{
	return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
}
long double solve(circle a, circle b)
{
	long double d = dis(a, b);
	if(d >= a.r + b.r) return 0;
	if(d <= fabs(a.r - b.r)){
		long double r = min(a.r, b.r);
		return PI * r * r;
	}
	long double ang1 = acos((a.r * a.r + d * d - b.r * b.r) / (2. * a.r * d)) * 2;
	long double ang2 = acos((b.r * b.r + d * d - a.r * a.r) / (2. * b.r * d)) * 2;	
	double tmp1 = 0.5 * ang1 * a.r * a.r - 0.5 * a.r * a.r * sin(ang1);	
	double tmp2 = 0.5 * ang2 * b.r * b.r - 0.5 * b.r * b.r * sin(ang2);
	long double ret = tmp1 + tmp2;
	return ret;
	
	
}

circle a[110];
int b[110];
double area[110];
int sgn(double x)
{
	if(fabs(x) < eps) return 0;
	return x > 0 ? 1 : -1;
}

int main()
{
	double r;
	int n;
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		double R;
		scanf("%d%lf%lf", &n, &R, &r);
		a[0].x = 0, a[0].y = 0; a[0].r = R- r;
		double ans = 0;
		for(int i = 1; i <= n; i ++){
			double x, y;
			scanf("%lf%lf", &x, &y);
			a[i].x = x; a[i].y = y;
			a[i].r = r;
			area[i] = solve(a[0], a[i]);
			gmax(ans, area[i]);
		}
		int num = 0;
		for(int i = 1; i <= n; i ++){
			if(sgn(area[i] - ans) == 0){
				b[++ num] = i;
			}
		}
		printf("%d\n", num);
		for(int i = 1; i < num; i ++) printf("%d ", b[i]); printf("%d\n", b[num]);
	}
	return 0;
}
/*
【trick&&吐槽】


【题意】


【分析】


【时间复杂度&&优化】


*/

  

posted @ 2017-11-13 01:30  Claris  阅读(677)  评论(0编辑  收藏  举报