"蔚来杯"2022牛客暑期多校训练营1
作者:互联网
链接
\(A : Villages: Landlines\)
做法一:将有交集的两个区间合并(显然若一个区间被覆盖可只建电塔使另一个区间被覆盖),两两相邻区间的最短距离的和即为最小值。
做法二:按发电站的位置向左右遍历,对于左边,按建筑区间的最右端位置排序,记录目前电力能到达的最左端,贪心更新,对于右边同理。注意,对左边,可能出现右边边建筑的范围过大使得左端点比发电站区间左端点还左的情况,所以要提前处理左端点的最小值,右边同理。
做法二:
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const LL N=4e5+3;
struct hh{
LL x,r;
bool operator<(const hh &a) const{
return x<a.x;}
}a[N];
LL n,pos,R,ans;
bool cmp1(hh &a,hh &b){
return a.x+a.r>b.x+b.r;
}
bool cmp2(hh &a,hh &b){
return a.x-a.r<b.x-b.r;
}
IL LL in(){
char c;LL f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
LL x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
int main()
{
n=in();
pos=in(),R=in();
for(LL i=1;i<n;++i) a[i]=(hh){in(),in()};
sort(a+1,a+n);
LL pp=lower_bound(a+1,a+n,(hh){pos,0})-a;
sort(a+1,a+pp,cmp1),sort(a+pp,a+n,cmp2);
LL l=1,r=pp,p1=pos-R,p2=pos+R;
while(l<pp&&a[l].x+a[l].r>=p1) p1=min(p1,a[l].x-a[l].r),p2=max(p2,a[l].x+a[l].r),++l;
while(r<n&&a[r].x-a[r].r<=p2) p1=min(p1,a[r].x-a[r].r),p2=max(p2,a[r].x+a[r].r),++r;
for(;l<pp;++l){
if(p1-a[l].x>a[l].r) ans+=p1-a[l].x-a[l].r;
p1=min(p1,a[l].x-a[l].r);
}
for(;r<n;++r){
if(a[r].x-a[r].r>p2) ans+=a[r].x-a[r].r-p2;
p2=max(p2,a[r].x+a[r].r);
}
cout<<ans<<endl;
return 0;
}
\(C :Grab the Seat!\)
将 \((0,1) , (0,m)\) 与坐人位置 \((x,y)\) 连线,\((x,y)\) 右边两线中间的区域就是被挡住的,且被挡住的必然是靠右的连续一段的座位。
发现两个点对彼此没啥影响,可以对两个点分开讨论,取未被遮挡座位的最小值。
考虑 \((0,1)\) ,按座位 \(y\) 坐标从小到大枚举,若某一点与 \((0,1)\) 连线的斜率小于前面行某点的斜率,则这条线会一直被前面的先覆盖,故我们只需维护斜率最大值计算即可。
具体实现时,由于萌新被浮点数整破防了,故用叉积判断斜率大小,为方便,可以在每一行最右端加一个坐人的座位。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1e6+3;
const LF eps=1e-6;
struct poi{
LL x,y;
poi operator-(const poi a) const{
return (poi){x-a.x,y-a.y};}
LL operator*(const poi a) const{
return x*a.y-y*a.x;}
};
struct hh{
LL id,x,y;
poi operator-(const poi a) const{
return (poi){x-a.x,y-a.y};}
}a[N];
bool cmp1(hh &a,hh &b){
return a.y<b.y;
}
int n,m,k,q,de[N],bo[N],re[N];
LL Min[N];
IL LL in(){
char c;LL f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
LL x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
void calc(int Max){
for(int i=1;i<=m;++i) Min[i]=n+1;
for(int i=1;i<=Max;++i) if(bo[a[i].id]) Min[a[i].y]=min(Min[a[i].y],a[i].x);
poi lp=(poi){1,-1},s1=(poi){0,1},s2=(poi){0,m};
int flag=0;
for(int i=1,j=0;i<=m;++i){
LL mi=n+1;
while(j<Max&&a[j+1].y<=i){
++j;
if(bo[a[j].id]) mi=min(mi,a[j].x);
}
poi x=(poi){mi,i-1};
if(lp*x>0) lp=x;
else Min[i]=min(Min[i],(1ll*(i-1)*lp.x-1)/lp.y+1);
}
lp=(poi){1,1};
for(int i=m,j=Max+1;i;--i){
LL mi=n+1;
while(j>1&&a[j-1].y>=i){
--j;
if(bo[a[j].id]) mi=min(mi,a[j].x);
}
poi x=(poi){mi,i-m};
if(lp*x<0) lp=x;
else Min[i]=min(Min[i],(1ll*(m-i)*lp.x-1)/(-lp.y)+1);
}
LL ans=0;
for(int i=1;i<=m;++i) ans+=Min[i]-1;
printf("%lld\n",ans);
}
int main()
{
n=in(),m=in(),k=in(),q=in();
for(int i=1;i<=k;++i) a[i]=(hh){i,in(),in()},bo[i]=1;
int cnt=k;
for(int i=1;i<=q;++i){
de[i]=in();
a[++cnt]=(hh){i+k,in(),in()};
}
sort(a+1,a+cnt+1,cmp1);
for(int i=1;i<=cnt;++i) re[a[i].id]=i;
for(int i=1;i<=q;++i) a[re[de[i]]].id=0,a[re[k+i]].id=de[i],re[de[i]]=re[k+i],calc(cnt);
return 0;
}
\(D : Mocha and Railgun\)
数学题,画个图列个式子就可以得出当直线 \(AB\) 与直线 \(MQ\) 重合时弧长最大。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF long double
using namespace std;
const LF pi=acos(-1.0);
LF x,y,r,d;
IL LL in(){
char c;LL f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
LL x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
int main()
{
int t=in();
while(t--){
r=in(),x=in(),y=in(),d=in();
LF dis=sqrt(1.0*x*x+1.0*y*y);
LF th1=acos((dis-d)/r),th2=acos((dis+d)/r);
printf("%.12LF\n",(th1-th2)*r);
}
return 0;
}
\(G : Lexicographical Maximum\)
设位数为 \(len\) ,直觉告诉我们 \(len-1\) 个 \(9\) 很有可能成为答案,除非 \(n\) 的前 \(len-1\) 也为 \(9\) ,此时答案为 \(n\)。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF long double
using namespace std;
const int N=1e6+3;
char s[N];
IL LL in(){
char c;LL f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
LL x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
int main()
{
scanf("%s",s+1);
int n=strlen(s+1),flag=1;
for(int i=1;i<n;++i)
if(s[i]!='9') flag=0;
if(flag) printf("%s",s+1);
else{
for(int i=1;i<n;++i)
printf("9");
}
return 0;
}
\(H : Fly\)
按位分析,对于某一个二进制位,值只可能是 \(0,1\) ,对应不取或取。
看到背包问题可以往多项式方向想,分治 \(FFT\) 求出背包方案数,对于 \(b_i,c_i\) 的限制可以逆向消除背物品。
从第 \(0\) 位到第 \(i\) 位,选择物品总价值为为 \(V\),考虑 \(V/2^i\) 与 \(V \mod 2^i\),前者可以向更高位转移,后者与 \(M \mod 2^i\) 的比较可得出 \(V/2^i = M/2^i\) 时 \(V\) 与 \(M\) 的大小关系。
设计状态 \(f_{i,j,k}\) 表示 到第 \(i\) 位,\(j=V \mod 2^i\) 且 \(k=V/2^i\) 时的方案数,设 \(M\) 最高位是 \(m\),
则 \(f_{m+1,0,0}\) 为答案。
求 \(f_i\) ,可用 \(f_{i-1}\) 与该层的背包方案数多项式卷积求得,感觉题解的做法不太好直接卷积,就先将第 \(i-1\) 层的数组转化成 \(i\) 层再卷积(也许是这个的锅?)\(f_{i-1.k,j} -> f_{i,pd(j \& 1,m_{i-1},k),j>>1}\),\(m_{i}\) 为 \(M\) 第 \(i\) 位的数值,\(pd(x,y,z)=x>y || (x==y \& z)\),即对大小进行讨论。
只有80分,留个坑以后有时间(有心情)再调吧。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=32e4+15,p=998244353,G=3,Gi=332748118;
int n,k,mm,len,M[70],a[N],b[N],c[N],d[N],_a[N],_b[N],_c[N],_d[N],r[N];
LL m;
int *f[N],g[N],h[2][80004],F[70][2][80004],w[N],Len[N];
vector<int>lim[70];
IL LL in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
LL x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
IL int ksm(int a,int b){
int c=1;
while(b){
if(b&1) c=1ll*c*a%p;
a=1ll*a*a%p,b>>=1;
}
return c;
}
IL void calc(int lim){
for(int i=0;i<lim;++i)
r[i]=(r[i>>1]>>1)|((i&1)*(lim>>1));
}
IL void NTT(int *a,int lim,int op){
calc(lim);
for(int i=0;i<lim;++i)
if(i<r[i]) swap(a[i],a[r[i]]);
for(int i=1;i<lim;i<<=1){
int wn=ksm(~op?G:Gi,(p-1)/(i<<1));
for(int j=0;j<lim;j+=i<<1){
int w=1;
for(int k=0;k<i;++k,w=1ll*w*wn%p){
int x=a[j+k],y=1ll*w*a[j+i+k]%p;
a[j+k]=mod(x+y),a[j+i+k]=mod(x-y+p);
}
}
}
if(op==-1){
int inv=ksm(lim,p-2);
for(int i=0;i<lim;++i) a[i]=1ll*a[i]*inv%p;
}
}
IL void mul(int *a,int *b,int *c,int n,int m){
int lim=1;
while(lim<n+m-1) lim<<=1;
memcpy(_a,a,4*n),memcpy(_b,b,4*m),
memset(_a+n,0,4*(lim-n)),memset(_b+m,0,4*(lim-m));
NTT(_a,lim,1),NTT(_b,lim,1);
for(int i=0;i<lim;++i) _c[i]=1ll*_a[i]*_b[i]%p;
NTT(_c,lim,-1);
for(int i=0;i<n+m-1;++i) c[i]=_c[i];
}
int fenzhi(int k,int l,int r){
if(l==r){
f[k]=new int [a[l]+1];
memset(f[k],0,4*a[l]+4);
f[k][0]=f[k][a[l]]=1;
return a[l]+1;
}
int mid=l+r>>1,l1=0,l2=0;
l1=fenzhi(k<<1,l,mid);
l2=fenzhi(k<<1|1,mid+1,r);
f[k]=new int [l1+l2-1];
mul(f[k<<1],f[k<<1|1],f[k],l1,l2);
return l1+l2-1;
}
IL int pd(int x,int y,int z){return (x>y)||(x==y&&z);}
int main()
{
int x,y;
n=in(),m=in(),k=in();
for(int i=1;i<=n;++i) len+=(a[i]=in());
fenzhi(1,1,n);
for(LL i=0,j=1;;++i,j<<=1)
if(j&m) M[i]=1;
else if(j>m){mm=i;break;}
for(int i=1;i<=k;++i) x=in(),y=in(),lim[y].push_back(a[x]);
for(int i=0;i<=len;++i) g[i]=f[1][i];
for(int j=0,ll=len;j<lim[0].size();ll-=lim[0][j],++j)
for(int k=lim[0][j];k<=ll;++k)
g[k]=mod(g[k]-g[k-lim[0][j]]+p);
for(int i=0;i<=len;++i) F[0][0][i]=g[i];
Len[0]=len;
for(int i=1;i<=mm;++i) Len[i]=(Len[i-1]>>1)+len;
for(int i=1;i<=mm;++i){
memset(g,0,sizeof(g));
for(int j=0;j<=len;++j) g[j]=f[1][j];
int ll=len;
for(int j=0;j<lim[i].size();ll-=lim[i][j],++j)
for(int k=lim[i][j];k<=ll;++k)
g[k]=mod(g[k]-g[k-lim[i][j]]+p);
memset(h,0,sizeof(h));
for(int j=0;j<=Len[i-1];++j)
for(int k=0;k<=1;++k)
h[pd(j&1,M[i-1],k)][j>>1]=mod(h[pd(j&1,M[i-1],k)][j>>1]+F[i-1][k][j]);
memset(w,0,sizeof(w));
mul(h[0],g,w,(Len[i-1]>>1)+1,len+1);
for(int j=0;j<=Len[i];++j) F[i][0][j]=w[j];
memset(w,0,sizeof(w));
mul(h[1],g,w,(Len[i-1]>>1)+1,len+1);
for(int j=0;j<=Len[i];++j) F[i][1][j]=w[j];
}
printf("%d\n",F[mm][0][0]);
return 0;
}
\(I : Chiitoitsu\)
最优策略是摸到一张牌,构成对子就随便打一单张出去,没有就扔掉。
故牌堆中有两种牌,一种是与手上单牌相同的牌,且牌堆里必然有三张相同的,另一种是摸到直接扔的牌。
令 \(f_{j,i}\) 为当手上有 \(j\) 个对子, 牌堆还剩 \(i\) 张牌时的期望步数。我们可以根据对子数量求出单牌数量,进而求出牌堆里两种牌的数量。
转移方程:
\((f_{j,i}+1)*(45-6j)/{(i+1)} -> f_{j-1,i+1}\)
$ (f_{j,i}+1)*(i+6j-35)/(i+1) -> f_{j,i+1},j\not= 7$
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=36,M=1e5+3,p=1e9+7;
int n,m,t[128],f[8][150],b[8][150],inv[150],bo[150];
char s[N];
IL LL in(){
char c;LL f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
LL x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
IL int ksm(int a,int b){
int c=1;
while(b){
if(b&1) c=1ll*c*a%p;
a=1ll*a*a%p,b>>=1;
}
return c;
}
IL int turn(char c2,char c1){
return t[c1]*9+c2-'0';
}
int main()
{
t['p']=1,t['s']=2,t['z']=3;
n=in();
for(int i=1;i<=136;++i) inv[i]=ksm(i,p-2);
for(int i=0;i<=136;++i) f[7][i]=0,b[7][i]=1;
for(int i=2;i<=122;++i)
for(int j=0;j<=7;++j)
if(b[j][i]){
int x=13-2*j,s=3*x;
if(i<s) break;
if(j==7){
if(i+1>=3) f[j-1][i+1]=mod(f[j-1][i+1]+1ll*(f[j][i]+1)*inv[i+1]%p*3%p),b[j-1][i+1]=1;
}
else{
if(j&&i+1>=s+6) f[j-1][i+1]=mod(f[j-1][i+1]+1ll*(f[j][i]+1)*inv[i+1]%p*(s+6)%p),b[j-1][i+1]=1;
f[j][i+1]=mod(f[j][i+1]+1ll*(f[j][i]+1)*inv[i+1]%p*(i+1-s)%p),b[j][i+1]=1;
}
}
for(int i=1;i<=n;++i){
scanf("%s",s+1);
memset(bo,0,sizeof(bo));
int x=0;
for(int j=1;j<=26;j+=2)
++bo[turn(s[j],s[j+1])];
for(int j=1;j<=34;++j)
if(bo[j]==2) ++x;
printf("Case #%d: %d\n",i,f[x][123]);
}
return 0;
}
\(J : Serval and Essay\)
将 \(x\) 作为基础并以此推导出的结论集合为 \(S_x\)。
结论:若 \(S_x\) 与 \(S_y\) 交集不为空,则一者为另一者的子集。
意会一下还是挺明显的吧(
于是我们可以维护 \(S\) 的集合,将边合并,若 \(S_y\) 只有 \(S_x\) 的入边,说明选了 \(S_x\) 就可以证明 \(S_y\)。
用 \(set\) 维护集合的出边,进行启发式合并,合并边直接将对应点入度减 \(1\) 即可。
最后每个集合的元素数最大值就是答案。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
struct hh{
int x,y;
};
int n,fa[N],siz[N],deg[N];
set<int>st[N];
set<int>::iterator it1,it2;
queue<hh>q;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
int find(int x){return x==fa[x]?x:find(fa[x]);}
IL void merge(int x,int y){
if(x==y) return;
if(st[x].size()<st[y].size()) swap(x,y);
fa[y]=x,siz[x]+=siz[y];
while(st[y].size()){
it1=st[x].lower_bound(*st[y].begin());
if(it1!=st[x].end()&&*st[y].begin()==*it1){
if(--deg[*it1]==1) q.push({x,*it1});
}
else st[x].insert(*st[y].begin());
st[y].erase(st[y].begin());
}
}
void solve(int id){
n=in();
for(int i=1;i<=n;++i) st[i].clear(),fa[i]=i,siz[i]=1;
for(int i=1;i<=n;++i){
deg[i]=in();int x;
for(int j=1;j<=deg[i];++j)
st[x=in()].insert(i);
if(deg[i]==1) q.push({x,i});
}
while(q.size()){
hh u=q.front();q.pop();
merge(find(u.x),find(u.y));
}
int ans=0;
for(int i=1;i<=n;++i) ans=max(ans,siz[find(i)]);
printf("Case #%d: %d\n",id,ans);
}
int main()
{
int t=in();
for(int i=1;i<=t;++i) solve(i);
return 0;
}
标签:int,蔚来,LL,多校,long,牛客,while,IL,define 来源: https://www.cnblogs.com/yiqiAtiya/p/16500509.html