牛客练习赛26游记
A-平面
题目大意:
在平面上画\(n(n\le10^9)\)个X,最多将平面分成几部分?
思路:
如果是画直线的话答案是\(\frac{n^2+n+2}2\)。
画X相当于画两条直线,因此答案是\(2n^2+n+1\)。
时间复杂度\(\mathcal O(1)\)。
源代码:
#include
#include
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
typedef long long int64;
int main() {
const int n=getint()*2;
printf("%lld\n",((int64)n*n+n+2)/2);
return 0;
}
B-烟花
题目大意:
有\(n(n\le10^5)\)个烟花,每个烟花代表着互不相同的颜色,对于第\(i\)个烟花,它有\(p_i\)的概率被成功点燃。求产生颜色的期望个数及产生恰好\(k(k\le200)\)种颜色的概率。
思路:
显然期望个数就是\(\sum p_i\)。
而产生恰好\(k\)种颜色的概率可以DP。
用\(f_{i,j}\)表示前\(i\)个烟花中成功点燃\(j\)个的概率,转移方程为\(f_{i,j}=f_{i-1,j-1}\times p_i+f_{i-1,j}\times(1-p_i)\)。
时间复杂度\(\mathcal O(nk)\)。
源代码:
#include
#include
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=1e5+1,M=201;
double p[N],f[N][M];
int main() {
const int n=getint(),m=getint();
for(register int i=1;i<=n;i++) {
scanf("%lf",&p[i]);
}
f[0][0]=1;
for(register int i=1;i<=n;i++) {
f[i][0]=f[i-1][0]*(1-p[i]);
for(register int j=1;j<=m;j++) {
f[i][j]=f[i-1][j]*(1-p[i])+f[i-1][j-1]*p[i];
}
}
double ans=0;
for(register int i=1;i<=n;i++) ans+=p[i];
printf("%.4f\n%.4f\n",ans,f[n][m]);
return 0;
}
C-城市规划
题目大意:
有一条\(n(n\le10^6)\)个点的链,各结点编号依次为\(1\sim n\)。另有\(m(m\le10^7)\)个限制\((x_i,y_i)\),要求你从链上去掉一些边,使得对于所有的限制,\(x_i\)与\(y_i\)均不连通。求最少删掉几条边。
思路:
如果我们假设结点\(1,2\)之间的边编号为\(2\),\(2,3\)之间的边编号为\(3\)。将约束条件从点转化成边,则\([x_i+1,y_i]\)之间一定有边要被删掉。
方便起见,下面我们用\(x_i\)表示前面的\(x_i+1\)。
对于每个\(y_i\)只保留最大的\(x_i\)。
从左往右扫一遍,记录删去的最右的边\(p\)。如果当前点是某个\(y_i\),且对应的\(x_i\le p\),说明\(x_i\sim y_i\)之间已经不连通了,这时不需要进行任何操作。如果\(x_i>p\),则将\(x_i\)作为新的\(p\),ans++
即可。
时间复杂度\(\mathcal O(n+m)\),但是比较卡常。
源代码:
#include
#include
#include
#include
#include
class MMapInput {
private:
char *buf,*p;
int size;
public:
MMapInput() {
register int fd=fileno(stdin);
struct stat sb;
fstat(fd,&sb);
size=sb.st_size;
buf=reinterpret_cast(mmap(0,size,PROT_READ,MAP_PRIVATE,fileno(stdin),0));
p=buf;
}
char getchar() {
return (p==buf+size||*p==EOF)?EOF:*p++;
}
};
MMapInput mmi;
inline int getint() {
register char ch;
while(!isdigit(ch=mmi.getchar()));
register int x=ch^'0';
while(isdigit(ch=mmi.getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=1e6+1;
int cnt[N],left[N];
int main() {
const int n=getint(),m=getint();
for(register int i=0;i
D-xor序列
题目大意:
一个长度为\(n(n\le10^5)\)的数列,\(q(q\le10^5)\)次询问,每次询问\(x\)能否通过与数列中的一些数进行异或运算使得最终结果为\(y\)。
思路:
线性基。
源代码:
#include
#include
#include
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=32;
int b[N];
int main() {
const int n=getint();
for(register int i=0;i>i&1) x^=b[i];
}
puts(x==0?"YES":"NO");
}
return 0;
}
E-树上路径
题目大意:
给出一个\(n\)个点的树,\(1\)号节点为根节点,每个点有一个权值。\(m(m\le10^5)\)次操作,操作包含以下\(3\)种:
- 将以\(x\)为根的子树内节点的权值加\(val\);
- 将\((x,y)\)路径上的节点权值加\(val\);
- 询问\((x,y)\)路径上节点的权值两两相乘的和。
思路:
首先考虑数列上的情况,对于区间\([l,r]\)。若我们记\(a\)为\([l,r]\)的和,\(b\)为\([l,r]\)的平方和,则答案为\(\frac{a^2-b}2\)。
可以使用线段树来维护,利用树链剖分将这种做法推广到树上。
时间复杂度\(\mathcal O(m\log^2 n)\)。
源代码:
#include
#include
#include
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
using int64=long long;
const int N=1e5+1,mod=1e9+7,inv=5e8+4;
int w[N],par[N],top[N],dep[N],size[N],son[N],dfn[N],id[N];
std::vector e[N];
inline void add_edge(const int &u,const int &v) {
e[u].emplace_back(v);
e[v].emplace_back(u);
}
void dfs(const int &x,const int &par) {
size[x]=1;
::par[x]=par;
dep[x]=dep[par]+1;
for(auto &y:e[x]) {
if(y==par) continue;
dfs(y,x);
size[x]+=size[y];
if(size[y]>size[son[x]]) {
son[x]=y;
}
}
}
void dfs(const int &x) {
dfn[x]=++dfn[0];
id[dfn[x]]=x;
top[x]=x==son[par[x]]?top[par[x]]:x;
if(son[x]) dfs(son[x]);
for(auto &y:e[x]) {
if(y==par[x]||y==son[x]) continue;
dfs(y);
}
}
class SegmentTree {
#define _left <<1
#define _right <<1|1
#define mid ((b+e)>>1)
private:
int tag[N<<2],sum[N<<2],ssum[N<<2];
void update(const int &p,const int &b,const int &e,const int &x) {
(tag[p]+=x)%=mod;
ssum[p]=((ssum[p]+(int64)x*x%mod*len(b,e)%mod)%mod+(int64)2*sum[p]%mod*x%mod)%mod;
sum[p]=(sum[p]+(int64)x*len(b,e))%mod;
}
void push_up(const int &p) {
sum[p]=(sum[p _left]+sum[p _right])%mod;
ssum[p]=(ssum[p _left]+ssum[p _right])%mod;
}
void push_down(const int &p,const int &b,const int &e) {
if(!tag[p]) return;
update(p _left,b,mid,tag[p]);
update(p _right,mid+1,e,tag[p]);
tag[p]=0;
}
inline int len(const int &b,const int &e) const {
return e-b+1;
}
public:
void build(const int &p,const int &b,const int &e) {
tag[p]=0;
if(b==e) {
sum[p]=w[id[b]];
ssum[p]=(int64)w[id[b]]*w[id[b]]%mod;
return;
}
build(p _left,b,mid);
build(p _right,mid+1,e);
push_up(p);
}
void modify(const int &p,const int &b,const int &e,const int &l,const int &r,const int &x) {
if(b==l&&e==r) {
update(p,b,e,x);
return;
}
push_down(p,b,e);
if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),x);
if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,x);
push_up(p);
}
std::pair query(const int &p,const int &b,const int &e,const int &l,const int &r) {
if(b==l&&e==r) {
return std::make_pair(sum[p],ssum[p]);
}
int ret0=0,ret1=0;
push_down(p,b,e);
if(l<=mid) {
const auto q=query(p _left,b,mid,l,std::min(mid,r));
(ret0+=q.first)%=mod;
(ret1+=q.second)%=mod;
}
if(r>mid) {
const auto q=query(p _right,mid+1,e,std::max(mid+1,l),r);
(ret0+=q.first)%=mod;
(ret1+=q.second)%=mod;
}
return std::make_pair(ret0,ret1);
}
#undef _left
#undef _right
#undef mid
};
SegmentTree t;
inline int query(int x,int y) {
int ret0=0,ret1=0;
while(top[x]!=top[y]) {
if(dep[top[x]]