Codeforces Round #660(CF1388)
前言
老年选手连div2都AK不了。。我好菜啊。。
A Captain Flint and Crew Recruitment
题意
定义正整数(x)为近似质数当且仅当(x)可以被表示为(p imes q (p,q 均为质数且 p eq q)) 。一个正整数(n),要求将(n)分成(4)个不同正整数的和,且至少其中3个是近似质数,有(t)组数据。(1leq tleq 1000,1leq nleq 200000) 。
题解
div2的前几题还是简单的。。最小的几个近似质数是6,10,14 。直接将这3个作为分n时的那三个近似质数,那么能够分的条件显然就是(n>30) ,然后把(36,40,44) 另外判断掉就可以了。
(Code)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e5+10;
int read(){
int 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;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n;
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d",&n);
if(n==36){
cout<<"YES"<<endl;
cout<<"5 6 10 15"<<endl;
}
else if(n==40){
cout<<"YES"<<endl;
cout<<"9 6 10 15"<<endl;
}
else if(n==44){
cout<<"YES"<<endl;
cout<<"6 7 10 21"<<endl;
}
else if(n>30){
cout<<"YES"<<endl;
printf("%d %d %d %d
",6,10,14,n-30);
}
else {
printf("NO
");
}
}
return 0;
}
B Captain Flint and a Long Voyage
题意
黑板上有一个(n) 位的数,把每一位转化成二进制后拼接在一起,再擦去最后(n) 位,要使最后得到的数尽量大,在此前提下使原数尽量小,问原来的数是多少,有t组数据。(1leq tleq 1000,sum{n} leq 200000) 。
题解
要使最后的数尽量大,位数要尽量多,所以原数肯定由(8) 和(9) 组成,要使原数小,让末尾几个会被擦到的数作为8,前面的数作为9即可。
(Code)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e5+10;
int read(){
int 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;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m;
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d",&n);
m=(n+3)/4;
for(int i=1;i<=n-m;++i) putchar('9');
for(int i=1;i<=m;++i) putchar('8');
puts("");
}
return 0;
}
C Uncle Bogdan and Country Happiness
题意
一颗(n) 个点的树,节点(1) 是根节点,一共有(m) 个居民,第(i) 个节点上住着(p_i) 个居民,刚开始所有居民在根节点出发沿最短路回到住处。刚开始每个居民的心情可能是开心或不开心。在路上居民的心情可能会从开心变成不开心,但不会从不开心变成开心,在同一个节点内心情不变。记(h_i) 为第(i) 个节点上经过的开心的人数减去不开心的人数的值。现在已知(n,m,p_i,h_i) ,问数据是否合理,有(t) 组数据。(1leq tleq 10000,1leq nleq 100000,sum{n} leq 200000, 0leq mleq 10^9, sum{p_i} = m ,-10^9leq h_ileq 10^9) 。
题解
在知道经过第(i) 个点的人数和(h_i) 后可以直接算出在这个点开心的人数和不开心的人数,这两个数都应是非负整数,然后(i) 的所有儿子的开心的人数总和不应超过(i) 的开心人数。
(Code)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+10;
int read(){
int 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;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m,cnt=1;
int hed[N],p[N],h[N],vis[N],siz[N],A[N],B[N];
bool flag;
struct edge{
int r,nxt;
}e[N<<1];
void insert(int u,int v){
e[++cnt].r=v;e[cnt].nxt=hed[u];hed[u]=cnt;
}
void J(int x){
vis[x]=1;
siz[x]=p[x];
int aa=0,bb=0;
for(int i=hed[x];i;i=e[i].nxt){
if(vis[e[i].r]) continue;
J(e[i].r);
siz[x]+=siz[e[i].r];
aa+=A[e[i].r];bb+=B[e[i].r];
}
if((h[x]+siz[x])%2!=0) {
flag=0;
return;
}
A[x]=(h[x]+siz[x])/2;
B[x]=siz[x]-A[x];
if(A[x]<0||B[x]<0) flag=0;
if(aa>A[x]) flag=0;
}
int main(){
int T,u,v;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);cnt=1;
for(int i=1;i<=n;++i) scanf("%d",&p[i]);
for(int i=1;i<=n;++i) scanf("%d",&h[i]);
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
insert(u,v);insert(v,u);
}
flag=1;
J(1);
if(!flag) printf("NO
");
else printf("YES
");
for(int i=1;i<=n;++i){
hed[i]=p[i]=h[i]=vis[i]=siz[i]=A[i]=B[i]=0;
}
}
return 0;
}
D Captain Flint and Treasure
题意
已知两个长度为(n) 的数组(a_i) 和(b_i) ,刚开始(ans=0) ,然后进行(n) 次操作,每次操作先选择一个数字(i(1leq ileq n)) ,使(ans) 增加(a_i) ,然后如果(b_i eq -1) ,使(a_{b_i}) 增加(a_i) 。(1)到(n)都只能选择一次。求ans的最大值和此时的操作方案。(1leq nleq 200000,-10^6leq a_ileq 10^6,1leq b_ileq n) 或 (b_i=-1) ,保证(b_i) 数组中的数不成环。
题解
因为(b_i) 中的数不成环,那么(b_i) 数组的关系是可以拓扑排序的。拓扑排序后若(a_i) 为正,显然将其加之下一个位置是较优的,按拓扑的顺序放即可。若(a_i)为负,则不应该加到下个位置中,放在操作序列中较后的位置。
(Code)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+10;
int read(){
int 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;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m,cnt=1;
int du[N],b[N],p[N];
LL a[N];
queue<int> Q;
LL ans=0;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%I64d",&a[i]);
for(int i=1;i<=n;++i) {
scanf("%d",&b[i]);
if(b[i]!=-1)du[b[i]]++;
}
for(int i=1;i<=n;++i) if(!du[i]) Q.push(i);
int l=1,r=n,x;
while(!Q.empty()){
x=Q.front();Q.pop();
ans+=a[x];
if(a[x]<0){
p[r]=x;--r;
if(b[x]!=-1){
--du[b[x]];
if(!du[b[x]]) Q.push(b[x]);
}
}
else{
p[l]=x;++l;
if(b[x]!=-1){
a[b[x]]+=a[x];
--du[b[x]];
if(!du[b[x]]) Q.push(b[x]);
}
}
}
printf("%I64d
",ans);
for(int i=1;i<=n;++i) printf("%d ",p[i]);puts("");
return 0;
}
E Uncle Bogdan and Projections
题意
已知(n) 个与坐标系的(OX) 轴平行且在(OX) 轴上方的线段,保证各线段无重合(端点可以重合)。现要使所有的线段沿某向量投影到(OX) 轴上,要求各线段的投影无重合(端点可以重合),并使所有投影的最右端点与最左端点的距离最小,求最小的距离值。(1leq nleq 2000,-10^6leq xl_i < xr_i leq 10^6,1leq y_ileq 10^6) ,结果误差在(10^-6)之间视为正确。投影方式如图。
题解
当时被这题卡自闭了。。即使是div2的几何题也好难啊。。
题中的向量可以用角度来表示。要求各投影不能重合,我们可以枚举两条高度不同的线段计算不允许的角度的区间。由此我们可得到(n^2) 级别数量的不允许的区间。显然最优的决策的向量的角度一定在这些区间的边界上。整合各个区间得到所有可能的向量角度。然后尝试所有的决策。直接算是(O(n)) 的,决策有(n^2) 个,总共的效率就是(O(n^3)) 的,这样显然是过不了的。。
有一个比较显然的结论,我们算出最右端点和最左端点的原点一定在原图的凸包上。可以提前处理出凸包。对所有的决策向量也进行排序,这样就可以在凸包上用指针来计算所有决策向量对应的最左和最右端点了。总效率是(O(n^2logn)) 。
(Code)
#include <bits/stdc++.h>
#define LL long long
#define DB long double
using namespace std;
const int N=2e3+10;
const DB inf=1e18;
int read(){
int 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;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m;
struct edge{
int x[2],y;
}e[N];
bool cmp(edge A,edge B){
return A.y<B.y;
}
DB ans1,ans2;
vector< pair<DB,int> > p;
vector<DB>p2,mn,mx;
struct P{
int x,y;
P(int xx=0,int yy=0){x=xx;y=yy;}
}lu,rd,a[N<<1];
P operator - (P A,P B){
return P(A.x-B.x,A.y-B.y);
}
LL cha(P A,P B){
return (LL)B.y*A.x-(LL)A.y*B.x;
}
bool cmpa(P A,P B){
if(atan2(A.y-a[1].y,A.x-a[1].x)!=atan2(B.y-a[1].y,B.x-a[1].x))
return atan2(A.y-a[1].y,A.x-a[1].x)>atan2(B.y-a[1].y,B.x-a[1].x);
if(A.y!=B.y) return A.y>B.y;
return A.x<B.x;
}
int main(){
int cnt=0;
scanf("%d",&n);m=0;
lu.x=2e9;lu.y=-2e9;rd.x=-2e9;rd.y=2e9;
for(int i=1;i<=n;++i){
scanf("%d%d%d",&e[i].x[0],&e[i].x[1],&e[i].y);
++m;a[m].x=e[i].x[0];a[m].y=e[i].y;
if(a[m].y>lu.y||(a[m].y==lu.y&&a[m].x<lu.x)) lu=a[m];
if(a[m].y<rd.y||(a[m].y==rd.y&&a[m].x>rd.x)) rd=a[m];
++m;a[m].x=e[i].x[1];a[m].y=e[i].y;
if(a[m].y>lu.y||(a[m].y==lu.y&&a[m].x<lu.x)) lu=a[m];
if(a[m].y<rd.y||(a[m].y==rd.y&&a[m].x>rd.x)) rd=a[m];
}
for(int i=1;i<=m;++i){
if(a[i].x==lu.x&&a[i].y==lu.y){
swap(a[i],a[1]);
break;
}
}
sort(a+2,a+m+1,cmpa);
cnt=2;
for(int i=3;i<=m;++i){
while((cnt>=2&&cha(a[i]-a[cnt-1],a[cnt]-a[cnt-1])==0)||
(cnt>=3&&cha(a[i]-a[cnt-2],a[cnt]-a[cnt-2])+cha(a[cnt]-a[cnt-2],a[cnt-1]-a[cnt-2])<=cha(a[i]-a[cnt-2],a[cnt-1]-a[cnt-2]))) --cnt;
a[++cnt]=a[i];
}
if(cnt>=3&&cha(a[cnt]-a[1],a[cnt-1]-a[1])==0) --cnt;
m=cnt;a[m+1]=a[1];
sort(e+1,e+1+n,cmp);
DB l=inf,r=-inf;
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
if(e[i].y==e[j].y) continue;
p.push_back({(DB)(e[i].x[0]-e[j].x[1])/(DB)(e[j].y-e[i].y),2});
p.push_back({(DB)(e[i].x[1]-e[j].x[0])/(DB)(e[j].y-e[i].y),1});
}
}
sort(p.begin(),p.end());
cnt=0;
for(int i=0;i<p.size();++i) {
if(p[i].second==2){
if(!cnt) p2.push_back(p[i].first);
++cnt;
}
else{
--cnt;
if(!cnt) p2.push_back(p[i].first);
}
}
if(!p2.size()) p2.push_back(0);
int now=m+1;
for(int i=0;i<p2.size();++i){
while((DB)a[now].y*p2[i]+(DB)a[now].x>=(DB)a[now-1].y*p2[i]+(DB)a[now-1].x&&now>1) --now;
mn.push_back((DB)a[now].y*p2[i]+(DB)a[now].x);
}
for(int i=1;i<=m;++i){
if(a[i].x==rd.x&&a[i].y==rd.y){
swap(a[i],a[1]);
break;
}
}
sort(a+2,a+m+1,cmpa);
a[m+1]=a[1];
now=m+1;
for(int i=0;i<p2.size();++i){
while((DB)a[now].y*p2[i]+(DB)a[now].x<=(DB)a[now-1].y*p2[i]+(DB)a[now-1].x&&now>1) --now;
mx.push_back((DB)a[now].y*p2[i]+(DB)a[now].x);
}
DB ans=mx[0]-mn[0];
for(int i=0;i<p2.size();++i){
if(mx[i]-mn[i]<ans) ans=mx[i]-mn[i];
}
printf("%0.8Lf
",ans);
return 0;
}