CF2038

A. Bonus Project

最多为 。从后往前考虑。如果到 个人时还有 的工作,就都罢工了;否则第 个人会尽量留给 做,且不让 罢工。取 减去 递归为 的子问题。

B. Make It Equal

时对所有 做一遍得到 ,所以 可二分。每次检查时暴力做若干轮,能减就减, 至少减半。

C. DIY

扔掉只出现一次的,排序。检查取 还是

D. Divide OR Conquer

对于右端点 ,合法的值 的左端点区间 只有 个。二分加 st 表找出来。。 扫描线 ,离散化后树状数组维护 的 dp 值之和。对于 的区间 在扫到 时减去 的 dp 值之和,扫到 时加上 的 dp 值之和,扫到 时加到树状数组里。

int n,a[maxn],ans;
int f[18][maxn];
int calc(int l,int r){
	int k=__lg(r-l+1);
	return f[k][l]|f[k][r-(1<<k)+1];
}
struct node{
	int l,r,x,f;
};
int lsh[maxn<<5],len;
vector<node> ask[maxn];
vector<pii> add[maxn],del[maxn];
void inc(int &u,int v){((u+=v)>=mod)&&(u-=mod);}
#define lb(x) (x&(-x))
int tree[maxn<<5];
void upd(int x,int w){while(x<=len)inc(tree[x],w),x+=lb(x);}
int que(int x){int res=0;while(x)inc(res,tree[x]),x-=lb(x);return res;}
void work(){
	n=read();
	for(int i=1;i<=n;i++)f[0][i]=a[i]=read();
	for(int j=1;j<18;j++){
		for(int i=1;i+(1<<j)-1<=n;i++)f[j][i]=f[j-1][i]|f[j-1][i+(1<<j-1)];
	}
	for(int i=1;i<=n;i++){
		int p=i;
		while(p){
			int l=1,r=p,res=p;
			while(l<=r){
				int mid=l+r>>1;
				if(calc(mid,i)==calc(p,i))res=mid,r=mid-1;
				else l=mid+1;
			}
			ask[i].pb({res,p,calc(p,i),0});
			lsh[++len]=ask[i].back().x;
			p=res-1;
		}
	}
	ask[0].pb({0,0,0,1});lsh[++len]=0;
	sort(lsh+1,lsh+len+1);len=unique(lsh+1,lsh+len+1)-lsh-1;
	for(int i=0;i<=n;i++){
		for(int j=0;j<ask[i].size();j++){
			ask[i][j].x=lower_bound(lsh+1,lsh+len+1,ask[i][j].x)-lsh;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<ask[i].size();j++){
			int l=ask[i][j].l-1,r=ask[i][j].r-1,x=ask[i][j].x;
			del[l].pb({i,j}),add[r].pb({i,j});
		}
	}
	for(int i=0;i<=n;i++){
		for(auto[i,j]:del[i])inc(ask[i][j].f,mod-que(ask[i][j].x));
		for(int j=0;j<ask[i].size();j++)upd(ask[i][j].x,ask[i][j].f);
		for(auto[i,j]:add[i])inc(ask[i][j].f,que(ask[i][j].x));
	}
	for(auto[l,r,x,f]:ask[n])inc(ans,f);
	printf("%lld\n",ans);
}

F. Alternative Platforms

降序,枚举最大值取到 ,枚举 ,要求在 中选 个且至少有一个 。容斥为减去所有都大于 的情况,记 差卷积。记 差卷积。交换 ,把大于等于改为严格大于再做一遍。

int n;
pii a[maxn];
int num[maxn];
int lsh[maxn<<1],len;
int tree[maxn<<1];
#define lb(x) (x&(-x))
void upd(int x,int w){
	while(x<=len){tree[x]+=w,x+=lb(x);}
}
int que(int x){
	int res=0;
	while(x)res+=tree[x],x-=lb(x);
	return res;
}
int fac[maxn],inv[maxn];
inline int ksm(int a,int b=mod-2){
	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
int C(int m,int n){
	if(m<n||m<0||n<0)return 0;
	return fac[m]*inv[n]%mod*inv[m-n]%mod;
}
int ans[maxn],val[maxn];
using ploy::mul;
void work(){
	n=read();
	for(int i=1;i<=n;i++)a[i].fi=read(),lsh[++len]=a[i].fi;
	for(int i=1;i<=n;i++)a[i].se=read(),lsh[++len]=a[i].se;
	sort(lsh+1,lsh+len+1);len=unique(lsh+1,lsh+len+1)-lsh-1;
	for(int i=1;i<=n;i++)a[i].fi=lower_bound(lsh+1,lsh+len+1,a[i].fi)-lsh;
	for(int i=1;i<=n;i++)a[i].se=lower_bound(lsh+1,lsh+len+1,a[i].se)-lsh;
	fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
	inv[n]=ksm(fac[n]);for(int i=n-1;~i;i--)inv[i]=inv[i+1]*(i+1)%mod;
	sort(a+1,a+n+1);reverse(a+1,a+n+1);
	vector<int> f(n+1),g(n+1);
	f[0]=0;for(int i=1;i<=n;i++)f[i]=lsh[a[i].fi]*fac[i-1]%mod;
	for(int i=0;i<=n;i++)g[i]=inv[n-i];
	f=mul(f,g);
	for(int k=1;k<=n;k++)ans[k]=f[k+n]*inv[k-1]%mod;
	// for(int i=1;i<=n;i++){
		// for(int k=1;k<=n;k++)(ans[k]+=lsh[a[i].fi]*C(i-1,k-1))%=mod;
	// }
	for(int i=1;i<=n;i++){
		int num=que(len)-que(a[i].fi);
		if(a[i].se>a[i].fi)(val[num]+=lsh[a[i].fi])%=mod;
		upd(a[i].se,1);
	}
	f.resize(n),g.resize(n+1);
	for(int i=0;i<n;i++)f[i]=mod-val[i]*fac[i]%mod;
	for(int i=0;i<=n;i++)g[i]=inv[n-i];
	f=mul(f,g);
	for(int k=1;k<=n;k++)(ans[k]+=f[n+k-1]*inv[k-1])%=mod;
	// for(int i=0;i<n;i++){
		// for(int k=1;k<=n;k++)(ans[k]+=mod-val[i]*C(i,k-1)%mod)%=mod;
	// }
	for(int i=0;i<=n;i++)val[i]=0;
	for(int i=0;i<=len;i++)tree[i]=0;
	for(int i=1;i<=n;i++)swap(a[i].fi,a[i].se);
	sort(a+1,a+n+1);reverse(a+1,a+n+1);
	f.resize(n+1),g.resize(n+1);
	f[0]=0;for(int i=1;i<=n;i++)f[i]=lsh[a[i].fi]*fac[i-1]%mod;
	for(int i=0;i<=n;i++)g[i]=inv[n-i];
	f=mul(f,g);
	for(int k=1;k<=n;k++)(ans[k]+=f[k+n]*inv[k-1])%=mod;
	// for(int i=1;i<=n;i++){
		// for(int k=1;k<=n;k++)(ans[k]+=lsh[a[i].fi]*C(i-1,k-1))%=mod;
	// }
	for(int i=1;i<=n;i++){
		int num=que(len)-que(a[i].fi-1);
		if(a[i].se>=a[i].fi)(val[num]+=lsh[a[i].fi])%=mod;
		upd(a[i].se,1);
	}
	f.resize(n),g.resize(n+1);
	for(int i=0;i<n;i++)f[i]=mod-val[i]*fac[i]%mod;
	for(int i=0;i<=n;i++)g[i]=inv[n-i];
	f=mul(f,g);
	for(int k=1;k<=n;k++)(ans[k]+=f[n+k-1]*inv[k-1])%=mod;
	// for(int i=0;i<n;i++){
		// for(int k=1;k<=n;k++)(ans[k]+=mod-val[i]*C(i,k-1)%mod)%=mod;
	// }
	for(int i=1;i<=n;i++)printf("%lld ",ans[i]*ksm(C(n,i))%mod);puts("");
}

G. Guess One Character

知道 的连续段数,问 能确定第一位。

I. Polyathlon

复制一遍,对每个 ,求 排序后的最大值。因为 内一定能区分,等价于对 排序。考虑基数排序,从后往前对每位排序,复杂度

int n,m;
vector<int> a[maxn];
char s[maxn];
int id[maxn];
void work(int j){
	vector<int> pos[2];
	for(int i=1;i<=n;i++)pos[a[id[i]][j]].pb(id[i]);
	n=0;
	for(int i:pos[0])id[++n]=i;
	for(int i:pos[1])id[++n]=i;
}
int ans[maxn];
void work(){
	n=read();m=read();
	for(int i=1;i<=n;i++)a[i].resize(m+1);
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		for(int j=1;j<=m;j++)a[i][j]=s[j]=='1';
	}
	for(int i=1;i<=n;i++)id[i]=i;
	for(int i=m-1;i;i--)work(i);
	for(int i=m;i;i--)work(i),ans[i]=id[n];
	for(int i=1;i<=m;i++)printf("%lld ",ans[i]);
}

J. Waiting for…

模拟。

K. Grid Walk

取出 个后缀最小值,在这些点组成的矩形上走。

L. Bridge Renovation

贪心,枚举几个 个 (。其他的填

M. Royal Flush

表示剩 张牌, 种花色的 各持 张, 是一个降序的 vector,里面只存没有被删过的状态的降序数量。枚举各种策略,枚举扔牌之后每种牌各进多少张,算概率,记搜。

注意取一个 的前缀转移不一定最优,例如当前 ,有 和一张 ,扔掉 之后一次只能摸一张扔一张,不如扔掉 。策略是枚举一个子集。常数极小,60ms 以内。

int n;
map<vector<int>,db> dp[55];
map<vector<int>,bool> vis[55];
db C(int m,int n){
	if(m<n||m<0||n<0)return 0;
	db ans=1;
	for(int i=1;i<=n;i++)ans=ans*(m-i+1)/(1.0*i);
	return ans;
}
db dfs(int dep,vector<int> a){
	if(dep<=0)return -1;
	if(a.front()==5)return -1;
	if(vis[dep][a])return dp[dep][a];vis[dep][a]=1;
	vector<int> aa=a;
	db ans=inf;
	int num=0;while(a.size()&&!a.back())a.pop_back(),++num;
	vector<int> aaa=a;
	for(int s=0;s<(1<<aaa.size());s++){
		a.clear();
		for(int i=0;i<aaa.size();i++)if(s&(1<<i))a.pb(aaa[i]);
		for(int i=1;i<=num;i++)a.pb(0);
		if(!a.size())continue;
		db res=0;
		int sum=0;for(int i:a)sum+=5-i;
		int in=5;for(int i:a)in-=i;
		if(!in)continue;
		for(int i=0;i<=(a.size()>0?5-a[0]:0);i++){
			if(i)a[0]+=i;
			for(int j=0;j<=(a.size()>1?5-a[1]:0)&&i+j<=in;j++){
				if(j)a[1]+=j;
				for(int k=0;k<=(a.size()>2?5-a[2]:0)&&i+j+k<=in;k++){
					if(k)a[2]+=k;
					for(int l=0;l<=(a.size()>3?5-a[3]:0)&&i+j+k+l<=in;l++){
						if(l)a[3]+=l;
						vector<int> aaaa=a;
						db val=C((a.size()>0?5-a[0]+i:0),i)*C((a.size()>1?5-a[1]+j:0),j)*C((a.size()>2?5-a[2]+k:0),k)*C((a.size()>3?5-a[3]+l:0),l)*C(dep-sum,in-i-j-k-l);
						val=val/C(dep,in);
						sort(a.begin(),a.end(),greater<int>());
						res+=val*(dfs(dep-in,a)+1);
						a=aaaa;
						if(l)a[3]-=l;
					}
					if(k)a[2]-=k;
				}
				if(j)a[1]-=j;
			}
			if(i)a[0]-=i;
		}
		ans=min(ans,res);
	}
	if(ans==inf)ans=-1;
	return dp[dep][aa]=ans;
}
void work(){
	n=read();
	vector<int> a(n,0);
	printf("%.9lf\n",dfs(13*n,a));
}

N. Fixing the Expression

改运算符优于改数字。