2022“杭电杯”中国大学生算法设计超级联赛(2)
链接
这次的界面终于让我有了一点我是花了钱的感觉(
\(Static Query on Tree\)
树链剖分做法:把在 \(A,B\) 中的点到根的路径染色,把 \(C\) 中的点的子树染色,都被染色过的点就是答案。
容斥做法:按 \(C\) 中节点的从属分成多个子树,若 \(a\) 在 \(b\) 的子树中,则无需考虑 \(a\)。求出 \(A,B,A\cap B\) 到 \(C\) 经过的点集大小,容斥即可。
虚树做法:直接构建 \(A,B,C\) 中的点的虚树,直接 \(DP\) 即可。
此外,前两种方法不适用于无向边,虚树做法直接换根即可。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define max(x,y) (x>y?x:y)
char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int N=2e5+3;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,fir[N],num,fa[N][20],dep[N],cnt,dfn[N],siz[N],sta[N],tp,bo[N][3],f[N][3];
int di[N],ans;
vector<int>E[N];
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;
}
bool cmp1(int x,int y){return dfn[x]<dfn[y];}
IL void add(int x,int y){
e[++num]=(hh){y,fir[x]},fir[x]=num;
}
IL void clear(){
num=cnt=0,memset(fir,0,sizeof(fir));
memset(fa,0,sizeof(fa));
}
IL void Add(int x,int y){
E[x].push_back(y);
}
void dfs1(int u,int f){
fa[u][0]=f,siz[u]=1,dfn[u]=++cnt,dep[u]=dep[f]+1;
for(int i=0;fa[u][i];++i)
fa[u][i+1]=fa[fa[u][i]][i];
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
dfs1(v,u),siz[u]+=siz[v];
}
IL int Lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=17;~i;--i)
if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=17;~i;--i)
if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
IL void ins(int x){
if(!tp){sta[++tp]=x;return;}
int lca=Lca(sta[tp],x);
if(lca==sta[tp]){sta[++tp]=x;return;}
while(dep[lca]<dep[sta[tp-1]]) Add(sta[tp-1],sta[tp]),--tp;
Add(lca,sta[tp]),--tp;
if(lca!=sta[tp]) sta[++tp]=lca;
sta[++tp]=x;
}
void dfs2(int u,int fa){
f[u][2]=f[fa][2]|bo[u][2];
for(int i=0;i<E[u].size();++i){
int v=E[u][i];
dfs2(v,u);
f[u][0]|=f[v][0],
f[u][1]|=f[v][1];
if(f[u][2]&&(f[v][0]&&f[v][1])) ans+=dep[v]-dep[u]-1;
}
f[u][0]|=bo[u][0],f[u][1]|=bo[u][1];
if(f[u][2]&&(f[u][0]&&f[u][1])) ++ans;
}
void dele(int u,int fa){
f[u][0]=f[u][1]=f[u][2]=bo[u][0]=bo[u][1]=bo[u][2]=0;
for(int i=0;i<E[u].size();++i) dele(E[u][i],u);
E[u].clear();
}
void solve(){
n=in(),m=in(),clear();
for(int i=2;i<=n;++i) add(in(),i);
dfs1(1,0);
int nn[3];
while(m--){
di[0]=ans=0;
for(int i=0;i<=2;++i) nn[i]=in();
for(int i=0;i<=2;++i)
for(int j=1;j<=nn[i];++j)
bo[di[++di[0]]=in()][i]=1;
sort(di+1,di+di[0]+1),di[0]=unique(di+1,di+di[0]+1)-di-1;
sort(di+1,di+di[0]+1,cmp1);
for(int i=1;i<=di[0];++i) ins(di[i]);
while(tp>1) Add(sta[tp-1],sta[tp]),--tp;
dfs2(sta[1],0),dele(sta[1],0);tp=0;
printf("%d\n",ans);
}
}
int main()
{
int T=in();
while(T--) solve();
return 0;
}
\(C++ to Python\)
只输出 \(()-,\) 与数字即可。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define max(x,y) (x>y?x:y)
using namespace std;
const int N=1e5+3;
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 chk(char c){return (c>='0'&&c<='9')||(c=='-')||(c=='(')||(c==')')||(c==',');}
void solve(){
string s,t;
cin>>s;
for(int i=0;i<s.size();++i)
if(chk(s[i])) t+=s[i];
cout<<t<<endl;
}
int main()
{
int T=in();
while(T--) solve();
return 0;
}
\(Copy\)
真的真的要仔细看数据范围啊。。。如果 \(l,r > n\) 就不太好做了。
很明显要倒序操作,由于 \(l,r \leq n\),我们只需维护前 \(n\) 个位置即可。
于是本来的加区间变成删区间,在 \(r\) 后的序列会向前挪 \(r-l+1\) 个位置。
于是,亲爱的 \(bitset\) 又来了呢~
此外还需注意下运算符的优先级。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
struct que{
int op,l,r;
}q[N];
int n,m,a[N];
bitset<N>bit1,bit2;
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;
}
void solve(){
n=in(),m=in(),bit1=bit2=0;
for(int i=1;i<=n;++i) a[i]=in();
for(int i=1;i<=m;++i){
q[i].op=in(),q[i].l=in();
if(q[i].op==1) q[i].r=in();
}
for(int i=1;i<=n;++i) bit2[i]=1;
while(m){
if(q[m].op==1){
bit1=(((bit1&(bit2<<q[m].r))>>(q[m].r-q[m].l+1)))^(bit1&(bit2>>(n-q[m].r)));
}
else bit1[q[m].l]=bit1[q[m].l]^1;
m--;
}
int ans=0;
for(int i=1;i<=n;++i)
if(bit1[i]) ans^=a[i];
printf("%d\n",ans);
}
int main()
{
int T=in();
while(T--) solve();
return 0;
}
\(Slayers Come\)
对于每个技能,我们可以求出它可打倒的怪物区间,线段树上二分或二分 \(+\) \(ST\) 表都可做到 \(O(nlogn)\) 。
于是问题变为给若干区间,求选择一些区间使数轴 \(1\) ~ \(n\) 的点全覆盖的方案数。
定义 \(f_i\) 为 \(1\) ~ \(i\) 的点全覆盖的方案数 ( \(i+1\) 不会被覆盖,\((i+2)\) ~ \(n\) 的点不一定被覆盖 ) 。
将区间按右端点从小到大排序,右端点相同的再按左端点从大到小排序,这样可以包含区间被覆盖的情况。
对于区间 \((l,r)\) ,对 \(f_r\) 的贡献为 \(\displaystyle \sum_{i=l-1}^{r}f_i\),注意这里算了 \(f_r\) 的值,以此涵盖区间被覆盖的情况。
而对于 \((0,l-2)\) ,该区间取不取对其的转移无影响,故而直接乘 \(2\) 。
用线段树维护。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define max(x,y) (x>y?x:y)
char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int N=2e5+3,p=998244353;
int n,m;
struct hh{
int a,b;
}mo[N];
struct kk{
int x,l,r;
}sk[N];
struct zz{
int l,r;
bool operator<(const zz &a) const{
return r^a.r?r<a.r:l>a.l;}
}a[N];
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;}
#define ls k<<1
#define rs k<<1|1
struct tree{
int Min[N<<2];
IL void pushup(int k){
Min[k]=min(Min[ls],Min[rs]);
}
int ask1(int k,int l,int r,int ll,int rr,int v){
if(l>=ll&&r<=rr){
if(Min[k]>=v||l==r) return l;
int mid=l+r>>1;
if(Min[k<<1|1]<v) return ask1(k<<1|1,mid+1,r,ll,rr,v);
else return ask1(k<<1,l,mid,ll,rr,v);
}
int mid=l+r>>1;
if(ll>mid) return ask1(k<<1|1,mid+1,r,ll,rr,v);
if(rr<=mid) return ask1(k<<1,l,mid,ll,rr,v);
int pos=ask1(k<<1|1,mid+1,r,ll,rr,v);
if(mo[pos].a-mo[pos-1].b<v) return pos;
return ask1(k<<1,l,mid,ll,rr,v);
}
int ask2(int k,int l,int r,int ll,int rr,int v){
if(l>=ll&&r<=rr){
if(Min[k]>=v||l==r) return r;
int mid=l+r>>1;
if(Min[k<<1]<v) return ask2(k<<1,l,mid,ll,rr,v);
else return ask2(k<<1|1,mid+1,r,ll,rr,v);
}
int mid=l+r>>1;
if(ll>mid) return ask2(k<<1|1,mid+1,r,ll,rr,v);
if(rr<=mid) return ask2(k<<1,l,mid,ll,rr,v);
int pos=ask2(k<<1,l,mid,ll,rr,v);
if(mo[pos].a-mo[pos+1].b<v) return pos;
return ask2(k<<1|1,mid+1,r,ll,rr,v);
}
}T1,T2;
struct Tree{
int sum[N<<2],mul[N<<2];
void build(int k,int l,int r){
sum[k]=0,mul[k]=1;
if(l==r) return;
int mid=l+r>>1;
build(ls,l,mid),build(rs,mid+1,r);
}
IL void Mul(int k,int x){sum[k]=1ll*sum[k]*x%p;mul[k]=1ll*mul[k]*x%p;}
IL void pushdown(int k){
if(mul[k]^1) Mul(ls,mul[k]),Mul(rs,mul[k]),mul[k]=1;
}
void insert(int k,int l,int r,int p,int x){
if(l==r){sum[k]=mod(sum[k]+x);return;}
int mid=l+r>>1;
pushdown(k);
if(p<=mid) insert(ls,l,mid,p,x);
else insert(rs,mid+1,r,p,x);
sum[k]=mod(sum[ls]+sum[rs]);
}
int ask(int k,int l,int r,int ll,int rr){
if(l>=ll&&r<=rr) return sum[k];
int mid=l+r>>1;
pushdown(k);
if(rr<=mid) return ask(ls,l,mid,ll,rr);
if(ll>mid) return ask(rs,mid+1,r,ll,rr);
return mod(ask(ls,l,mid,ll,rr)+ask(rs,mid+1,r,ll,rr));
sum[k]=mod(sum[ls]+sum[rs]);
}
void modify(int k,int l,int r,int ll,int rr,int v){
if(l>=ll&&r<=rr) return Mul(k,v);
int mid=l+r>>1;
pushdown(k);
if(ll<=mid) modify(ls,l,mid,ll,rr,v);
if(rr>mid) modify(rs,mid+1,r,ll,rr,v);
sum[k]=mod(sum[ls]+sum[rs]);
}
}T;
void build(int k,int l,int r){
if(l==r){T1.Min[k]=mo[l].a-mo[l-1].b,T2.Min[k]=mo[l].a-mo[l+1].b;return;}
int mid=l+r>>1;
build(ls,l,mid),build(rs,mid+1,r);
T1.pushup(k),T2.pushup(k);
}
void solve(){
n=in(),m=in();
for(int i=1;i<=n;++i) mo[i]=(hh){in(),in()};mo[n+1].b=0;
for(int i=1;i<=m;++i) sk[i]=(kk){in(),in(),in()};
build(1,1,n);
for(int i=1;i<=m;++i){
int l=T1.ask1(1,1,n,1,sk[i].x,sk[i].l),r=T2.ask2(1,1,n,sk[i].x,n,sk[i].r);
a[i]=(zz){l,r};
}
sort(a+1,a+m+1),T.build(1,0,n),T.insert(1,0,n,0,1);
for(int i=1;i<=m;++i){
int v=T.ask(1,0,n,a[i].l-1,a[i].r);
T.insert(1,0,n,a[i].r,v);
if(a[i].l-2>=0) T.modify(1,0,n,0,a[i].l-2,2);
}
printf("%d\n",T.ask(1,0,n,n,n));
}
int main()
{
int T=in();
while(T--) solve();
return 0;
}
\(Bowcraft\)
直接被整破防。。。\(long double\) 不能过, \(double\) 能过。。。
\(f_i\) 为升到 \(i\) 级的期望值。
对于值为 \(\alpha,\beta\) 的技能书,当前级别为 \(i\) ,若选则 \(f_{i+1}=f_i+1+(1-\alpha)(1-\beta)(f_{i+1}-f_i)+(1-\alpha)\beta f{i+1}\),若不选则 \(f_{i+1}=f_{i+1}+1\),选与不选取决于二者的大小关系。
比较得若后者大于等于前者,则 \(f_{i+1} \geq \frac{\alpha + \beta +\alpha \beta}{\alpha} f_i\) ,故最优解必然是取 \(\frac{\alpha + \beta +\alpha \beta}{\alpha}\) 最小的前几个,将其排序后枚举。
转移方程 \(f_{i+1}=\frac{AB+f_i \sum{\frac{\alpha + \beta +\alpha \beta}{\alpha}} }{\sum \alpha}\)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1003,M=103;
const LF inf=1e9;
int n,m,A,B;
LF f[N];
struct kk{
int i,j;LF val;
bool operator<(const kk &a) const{
return val<a.val;}
}p[M*M];
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;
}
IL void solve(){
n=in(),A=in(),B=in(),m=A*B;
for(int i=1;i<=B;++i) p[i]=(kk){0,i-1,inf};
for(int i=1;i<A;++i)
for(int j=0;j<B;++j)
p[i*B+j+1]=(kk){i,j,(1.0*j*(A-i))/(i*B)};
sort(p+1,p+m+1);
for(int i=0;i<n;++i){
LF pl=0,pr=0,Min=inf;
for(int j=1;j<=m;++j){
pl+=1.0*p[j].i/A,pr+=1.0*(1.0*p[j].i*B+1.0*p[j].j*A-1.0*p[j].i*p[j].j)/m;
Min=min(Min,(m+pr*f[i])/pl);
}
f[i+1]=Min;
}
printf("%.3lf\n",f[n]);
}
int main()
{
int T=in();
while(T--) solve();
return 0;
}
\(Snatch Groceries\)
对区间排序后讨论即可。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
const LL inf=1e18;
struct hh{
int l,r;
bool operator<(const hh &a) const{
return l<a.l;}
}a[N];
int n;
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;
}
void solve(){
n=in();
for(int i=1;i<=n;++i) a[i]=(hh){in(),in()};
sort(a+1,a+n+1);
int ans=0;
for(int i=2;i<=n;++i)
if(a[i].l>a[i-1].r) ++ans;
else break;
if(ans==n-1) ans=n;
printf("%d\n",ans);
}
int main()
{
int T=in();
while(T--) solve();
return 0;
}
\(ShuanQ\)
显然 \(PQ-1=kM\) ,枚举 \(PQ-1\) 的约数并判断是否是质数即可。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
const LL inf=1e18;
int n,m,e;
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;
}
IL int chk(LL x){
if(x==1) return 0;
LL lim=sqrt(x);
for(LL i=2;i<=lim;++i)
if(x%i==0) return 0;
return 1;
}
void solve(){
LL x,y,z,e,Max;
x=in(),y=in(),e=in(),z=x*y-1;
Max=max({x,y,e});
for(LL i=1;i*Max<z;++i)
if(z%i==0){
LL m=z/i;
if(chk(m)) return (void)(printf("%lld\n",e*y%m));
}
printf("shuanQ\n");
}
int main()
{
int T=in();
while(T--) solve();
return 0;
}
\(DOS Card\)
合理设置节点储存的数据在线段树上直接嗯合并即可(这题没有修改操作真的很奇怪)。
变量名十分直观,合并十分浅显易懂(((
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
const LL inf=1e18;
int n,m;
LL val[N];
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;
}
struct node{
LL i,j,ii,jj,ij,ji,iij,iji,ijj,jij,iijj,ijij;
node operator+(const node &a) const{
node c;
c.i=max(i,a.i),
c.j=max(j,a.j),
c.ii=max({i+a.i,ii,a.ii}),
c.jj=max({j+a.j,jj,a.jj}),
c.ij=max({i+a.j,ij,a.ij}),
c.ji=max({j+a.i,ji,a.ji}),
c.iij=max({i+a.ij,ii+a.j,iij,a.iij}),
c.iji=max({i+a.ji,ij+a.i,iji,a.iji}),
c.ijj=max({i+a.jj,ij+a.j,ijj,a.ijj}),
c.jij=max({j+a.ij,ji+a.j,jij,a.jij}),
c.iijj=max({i+a.ijj,ii+a.jj,iij+a.j,iijj,a.iijj}),
c.ijij=max({i+a.jij,ij+a.ij,iji+a.j,ijij,a.ijij});
return c;
}
IL LL get(){return max(iijj,ijij);}
};
struct tree{
#define ls k<<1
#define rs k<<1|1
node a[N<<2];
void build(int k,int l,int r){
if(l==r){a[k]={val[l]*val[l],-val[l]*val[l],-inf,-inf,-inf,-inf,-inf,-inf,-inf,-inf,-inf,-inf};return;}
int mid=l+r>>1;
build(ls,l,mid),build(rs,mid+1,r);
a[k]=a[ls]+a[rs];
}
node query(int k,int l,int r,int ll,int rr){
if(l>=ll&&r<=rr) return a[k];
int mid=l+r>>1;
if(rr<=mid) return query(ls,l,mid,ll,rr);
if(ll>mid) return query(rs,mid+1,r,ll,rr);
return query(ls,l,mid,ll,rr)+query(rs,mid+1,r,ll,rr);
}
}T;
void solve(){
n=in(),m=in();
for(int i=1;i<=n;++i) val[i]=in();
T.build(1,1,n);
while(m--){
int l=in(),r=in();
printf("%lld\n",T.query(1,1,n,l,r).get());
}
}
int main()
{
int T=in();
while(T--) solve();
return 0;
}
\(Luxury cruise ship\)
注意到加 \(7\) 的次数不会超过 $31 \times 365 $ 次,
加 \(31\) 的次数不会超过 \(7 \times 365\) 次。
与可以对 \(N\) 模 \(3 \times 7 \times 31 \times 365\) ,减去的天数直接用 \(365\) 去除,剩下的用背包即可。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define max(x,y) (x>y?x:y)
using namespace std;
const int N=1e5+3,M=237615;
int f[M+1];
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 void init(){
memset(f,63,sizeof(f));
f[0]=0;
for(int i=1;i<=M;++i){
if(i>=7) f[i]=min(f[i],f[i-7]+1);
if(i>=31) f[i]=min(f[i],f[i-31]+1);
if(i>=365) f[i]=min(f[i],f[i-365]+1);
}
}
void solve(){
LL x=in(),y=x/M;
x%=M;
if(f[x]>1e9){printf("-1\n");}
else printf("%lld\n",f[x]+y*31*7*3);
}
int main()
{
init();
int T=in();
while(T--) solve();
return 0;
}