2025ing

博客园 首页 新随笔 联系 订阅 管理

感想

我纯废物
本场的题并非特别难
只是自己码力不及

题面:Link

T1

题面

有一个有 \(m\) 个格子的表格和 \(n\) 个下标
表格初始为空
从前到后顺次将这 \(n\) 个下标对应的表格位置涂上颜色
如果当前下标对应的位置已被涂色
就一直向找到空位并涂色
设第 \(i\) 个下标涂色位置为 \(pos_i\)
如果没有空位,则将 \(pos_i\) 记作 \(0\)
输出 \((1*pos_1+2*pos_2+...+n*pos_n)\%1000000007\) 的值
\(1<=N,M<=3000000\)

解析

这题场切了
首先容易想到 \(O(n\log^2{n})\) 的做法:树状数组+二分
但这样会 T
想到之前做根号线段树的并查集优化
发现这题也可以这样弄
然后就可以了

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3000009;
const ll Mod=1000000007;
int n,m,f[N];
ll ans=0;
int F(int i){
	if(f[i]==i)return i;
	return f[i]=F(f[i]);
}
int main(){
	int tmp;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)f[i]=i;
	for(int i=1;i<=n;i++){
		scanf("%d",&tmp);
		tmp=F(tmp);
		if(tmp<1)continue;
		f[tmp]=tmp-1;
		ans=(ans+(ll)i*tmp)%Mod;
	}
	printf("%lld\n",ans);
	return 0;
}

T2

题面

将一颗树上的每个节点共染上 \(M\) 种颜色
将同种颜色的点之间的路径上的边边权 \(+1\)
构造一种染色方案使边权大于 \(K\) 的边数刚好为 \(S\)
如无解,输出 \(No\)

解析

本题是全场最难题目
我们遍历每一条边

Code


T3

题面

给出一个只由 \(ABCD\) 组成的字符串 \(S\)
要求实现两种操作

  • \(1\ L\ R\)
    对于 \([L,R]\) 这一段字符串
    \(A\) 变成 \(B\) ,将 \(B\) 变成 \(C\) ,将 \(C\) 变成 \(D\) ,将 \(D\) 变成 \(A\)
  • \(2\ L\ R\ K\)
    求将 \([L,R]\) 这一段字符串划分成 \(K\) 段字符串,且其中字符的字典序单调上升的总方案数
    例如 \(AB\ ABC\ CD\ D\ AC\) 等都是合法的字符串
    将方案数 \(\mod{998244353}\) 输出

解析

本题作者场上题目看错了直接空着
看到区间操作能想到线段树
思考第二部分怎么求
我们发现对于一个字符串
有的部位是必须进行分割的
\(ACBDAA\)
其中 \(CB\ DA\ AA\) 处都必须进行分割
我们设这样的位置有 \(Cut\)
题目说我们要分 \(K\)
其实就是要让我们找 \(K-1\) 个分割点
实际上我们能定的只有 \(K-Cut-1\)
而一共可选的呢? \(R-L+1-1-Cut=R-L+Cut\)
所以我们组合数学

\[C^{K-Cut-1}_{R-L-Cut}=\frac{(R-L-Cut)!}{(K-Cut-1)!(R-L-K+1)!}\mod 998744353 \]

阶乘我们直接算显然就 T 了
所以我们预处理出每一个阶乘
然后 \(exGCD\) / 费马小定理求逆元即可
所以我们需要做的就是求区间 \(Cut\)
有一个暴力思路是直接维护区间 \(AA\ AB\ AC\ ......\) 的个数
然后还需要维护每段区间的左右端点以处理区间合并问题
然而并没有不暴力的思路
然后好像就可以了

Code

#include<bits/stdc++.h>
#define install int ls=i<<1,rs=i<<1|1,mid=(l+r)>>1
#define partall int ls=i<<1,rs=i<<1|1
using namespace std;
typedef long long ll;
const int N=100009;
const ll M=998244353;

//Scan & Pre Part
int n,T;
char k[N];
ll Shit[N];
void Scan(){
	scanf("%d%d%s",&n,&T,k+1),Shit[0]=1;
	for(int i=1;i<=n;i++)Shit[i]=(Shit[i-1]*i)%M;
}

//ExGCD Part
//ax % M = 1
//ax+bM=GCD(a,b)
//ax+by=GCD(a,b)=GCD(b,a%b)=bX+(a-a/b*b)Y=aY+b(X-a/b*Y)
void ExGCD(ll a,ll b,ll &x,ll &y){
	if(b==0){x=1,y=0;return;}
	ExGCD(b,a%b,y,x),y-=a/b*x;
}
int Inv(int i){
	ll x,y;
	ExGCD(Shit[i],M,x,y);
	return (x%M+M)%M;
}

class Segment{
	public:
		void Build(int i=1,int l=1,int r=n){
			if(l==r){lim[i]=rim[i]=k[l]-'A';return;}
			install;Build(ls,l,mid),Build(rs,mid+1,r);
			Pu(i);
		}
		void Modify(int i,int l,int r,int L,int R){
			if(l==L&&r==R){Ad(i,1),add[i]++;return;}
			Pd(i,l,r);
			install;
			if(R<=mid)Modify(ls,l,mid,L,R);
			else if(L>mid)Modify(rs,mid+1,r,L,R);
			else Modify(ls,l,mid,L,mid),Modify(rs,mid+1,r,mid+1,R);
			Pu(i);
		}
		int Query(int i,int l,int r,int L,int R){
			if(l==L&&r==R)return Cut(i);
			Pd(i,l,r);
			install;
			if(R<=mid)return Query(ls,l,mid,L,R);
			else if(L>mid) return Query(rs,mid+1,r,L,R);
			else return Query(ls,l,mid,L,mid)+Query(rs,mid+1,r,mid+1,R)+((rim[ls]>=lim[rs])?1:0);
		}
		void Show(int i=1,int l=1,int r=n){
			for(int it=l;it<=r;it++)putchar(k[it]);
			printf(" [%c,%c]%d\n",lim[i]+'A',rim[i]+'A',Cut(i));
			if(l==r){return;}
			Pd(i,l,r);
			install;
			Show(ls,l,mid),Show(rs,mid+1,r);
		}
	private:
		int tag[N<<2][4][4],lim[N<<2],rim[N<<2],add[N<<2];
		//A0 B1 C2 D3
		void Pu(int i){
			partall;
			for(int x=0;x<4;x++)for(int y=0;y<4;y++)tag[i][x][y]=tag[ls][x][y]+tag[rs][x][y];
			tag[i][rim[ls]][lim[rs]]++;
			lim[i]=lim[ls],rim[i]=rim[rs];
		}
		void Ad(int i,int d){
			int tmp[4][4];
			for(int x=0;x<=3;x++)for(int y=0;y<=3;y++)tmp[x][y]=tag[i][x][y];
			for(int x=0;x<=3;x++)for(int y=0;y<=3;y++)tag[i][(x+d)%4][(y+d)%4]=tmp[x][y];
			lim[i]=(lim[i]+d)%4,rim[i]=(rim[i]+d)%4;
		}
		void Pd(int i,int l,int r){
			if(!add[i]||l==r)return;
			partall;
			add[ls]+=add[i],add[rs]+=add[i],Ad(ls,add[i]),Ad(rs,add[i]),add[i]=0;
		}
		int Cut(int i){int ans=0;for(int x=0;x<4;x++)for(int y=0;y<=x;y++)ans+=tag[i][x][y];return ans;}
}t;
int main(){
	Scan(),t.Build();
	int op,l,r,d,Cut,mm,nn;
	while(T--){
//		t.Show();
//		puts("---------------------------");
		scanf("%d%d%d",&op,&l,&r);
		if(op==1){
			t.Modify(1,1,n,l,r);
		}else{
			scanf("%d",&d);
			Cut=t.Query(1,1,n,l,r);
			mm=d-Cut-1,nn=r-l-Cut;
			if(mm>nn)puts("0");
			else printf("%lld\n",Shit[nn]*Inv(mm)%M*Inv(nn-mm)%M);
			//printf("C(%d,%d)(%d)=%lld\n",mm,nn,Cut,Shit[nn]*Inv(mm)%M*Inv(nn-mm)%M);
		}
	}
}

T4

题面

给定一个容量为 \(M\) 的背包和 \(N\) 种物品 \((1<=N,M<=3000)\)
每种物品都有无限个
每个物品有两个属性 \(A_i\)\(B_i\)
物品的价值定义为 \(\alpha_j*A_i\ (1<=j<=10)\)
其中满足 \(\frac{j-1}{10}<\frac{B_i}{A_i}<=\frac{j}{10}\)
\(\alpha_1\)\(\alpha_{10}\) 会提前给出,且我们保证 \(\alpha_{i-1}<=\alpha_i<=\alpha_{i+1}\)
我们可以合并两个物品 \(Item_i\)\(Item_j\)
得到的物品 \(Item_k\) 满足 \(A_k=A_i+A_j\)\(B_k=B_i+B_j\)
你可以随意合并物品
求最终背包能够装下的最大价值

解析

这题听完 TJ 极为简单
要不我纯废物呢
这题看这个题面是很裸的背包
而且我们发现一个性质:需要让 \(\alpha\) 的等级尽量高
而其实一个物品说白了只有两维:等级 \(\alpha\) 和质量 \(A\)
所以我们润题解思考得出可以设一维质量求能获得的最高等级
继而确定其价值然后再完全背包求可以了

具体过程:
\(F_i\) 为在 \(\sum{A}\)\(i\) 的时候能获得的最大的 \(\sum{B}\)
易得出 \(F_i=max(F_i,F_{i-A_j}+B_j)\)
接下来将每一个 \(F_i\) 看作一样物品
跑完全背包即可
时间复杂度 \(O(nm+m^2)\)

关于完全背包

朴素完全背包 (时空 \(O(n^3)\))

int dp[][],val[],weight[]
for i 1->n
  for j weight[i]->m
    dp[i][j]=dp[i-1][j]
    for k k*weight[i]<=j
      dp[i][j]=max(dp[i][j],dp[i-1][j-weight[i]*k]+val[i]*k)

空间优化完全背包 (时 \(O(n^3)\)\(O(n^2)\))

int dp[],val[],weight[]
for i 1->n
  for j m->weight[i]
    for k k*weight[i]<=j
      dp[j]=max(dp[j],dp[j-weight[i]*k]+val[i]*k)

时空优化完全背包(时空 \(O(n^2)\))

int dp[],val[],weight[]
for i 1->n
  for j weight[i]->m
      dp[j]=max(dp[j],dp[j-weight[i]]+val[i])

Code

#include<bits/stdc++.h>
using namespace std;
const int N=3024;
int A[N],B[N],F[N],DP[N],val[13];
int alpha(int a,int b){
	if(a==0)return 0;
	for(int i=1;i<=10;i++)if(a*10>(i-1)*b&&10*a<=i*b)return val[i];
	puts("Shit"),exit(0);
}
int main(){
	int T,n,m;
	scanf("%d",&T);
	while(T--){
		for(int i=1;i<=10;i++)scanf("%d",&val[i]);
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)F[i]=-1e8-99,DP[i]=0;
		for(int i=1;i<=n;i++)scanf("%d%d",&A[i],&B[i]);
		for(int i=1;i<=n;i++)for(int j=A[i];j<=m;j++)F[j]=max(F[j],F[j-A[i]]+B[i]);
		for(int i=1;i<=m;i++)for(int j=i;j<=m;j++)if(F[i]>0)DP[j]=max(DP[j],DP[j-i]+i*alpha(F[i],i));
		for(int i=1;i<=m;i++)DP[i]=max(DP[i],DP[i-1]);
		printf("%d\n",DP[m]);
	}
	return 0;
}

TIPS

代码中的 \(F\) 数组只能是精确值,所以要附极小值
\(DP\) 无此要求

不理解的话看一下此数据
Imput:
1
1 1000
998 499

Output:
499

Wrong Answer:
500

posted on 2025-03-24 19:49  2025ing  阅读(29)  评论(0)    收藏  举报