2017 CCPC Qinhuangdao Site

时间:2022-02-24 03:43:14

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&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */