2024 福建省队集训录

文章可能补全方向:补全缺漏题面,以及做法和代码。还有就是单题的格式按照 pku 营那份写,现在这样太冗长了

Day1 #

47+0+25=72rk.20,被 T1 给创了。

T1 (placement)#

SourceP5225。赛事过了 1,3,4,7 这些点。Task 8 太困难了加上本菜鸡时间不充足,理解了下就贺了。

  • Task 1,暴力即可。

  • Task 2,考虑贪心加调整。每个问题选尽量优的 TPU ,即对每个问题,更优的 TPU 选择的概率更大一些。多调几次就过了。

  • Task 3,简单 dp,记 fi,x,y,z 表示到 i,三个机器内的和分别为 x,y,z 可不可行。数组上界取 .ans 中的 106 即可。

  • Task 4,简单 dp,记下上个点的颜色即可,注意 133,266 处有俩断点。

  • Task 5,赛事注意力涣散了,注意到每个点只和前 5 个点有关,且 k=5dp 记下前 5 个即可,复杂度 O(nk6)

  • Task 6,同 Task 2 一样贪心,但是每个直接选最小的就过了,好逆天啊。

  • Task 7,注意到 m=0,所有 t>1000,但答案是 1014,于是那么我们可以把不超过 1014 的时间当作边,二分图匹配输出方案即可。

  • Task 8,发现这个问题其实是一块一块的,1502 各独自是一块,中间每 50 个问题是一块,要计算下一块的任何问题,必须计算完上一块的所有问题。
    在块内,和 Task 7 一样,但是没有了时间限制,但显然时间限制是单调的,我们可以二分。
    观察到传输数据的时间都为 0/1,而且显然是要传输数据的,所以块间耗时就是 1,这是定值,不用考虑。
    但是,据 hhoppitree 所言,爬山能过。

  • Task 9/10,我赛事就没感觉到这个是 NP-Hard 的。退火/爬山/随机化/调整,感受那股劲。
    按照 hhoppitree 的谆谆教诲写了爬山,跑起来挺优秀的啊,优秀性我不会论证。
    updTask 9 可能饱和,请自行优化抽奖方式。

爬山
#include<bits/stdc++.h>
#define P pair<int,int>
#define fi first
#define se second
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define LL long long
using namespace std;
mt19937 rnd(time(0));
const int N=105;
int n,m,k,o,t[N][N],r[N][N],a[N],b[N],p[N],ans;
basic_string<int>g[N];
inline int get()
{
	int score;
	FILE *f=fopen("placement10.out","w");
	for(int i=1;i<=n;i++)
	{
		fprintf(f,"%d ",a[i]);
	}
	fclose(f);
	system("simulator.exe placement10.in placement10.out placement10.ans >fw.txt");
	FILE *g = fopen("fw.txt","r");
	fscanf(g,"%d",&score);
	fclose(g);return score;
}
int main()
{
	freopen("placement10.in","r",stdin);
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m>>k>>o;
	for(int i=1,u,v;i<=m;i++) cin>>u>>v,g[u]+=v;
	for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) cin>>t[i][j];
	for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) cin>>r[i][j];
	for(int i=1;i<=n;i++) a[i]=b[i]=rnd()%k+1;ans=get();
	while(1)
	{
		int w=rnd()%n+1,x=rnd()%k+1,las=a[w];
		a[w]=x;int nw=get();
		if(nw<ans)
		{
			ans=nw;memcpy(b,a,sizeof(a));
			cerr<<nw<<"\n";
			if(ans<=5142063) return 0;
		}
		else a[w]=las;
	}
	return 0;
}//5142063

T2 (cchoing)#

Sourceuoj #427

你先别急

T3 (meeting)#

Sourceuoj #705

你先别急


Day2 #

预期得分:100+10+0=110,实际得分:35+2+0=37rk.33

T1 忘记判 1 挂了 65 分,警钟长鸣!今天搬了三道集训队胡策的题。

T1 (compete)#

Sourceqoj 5034

正解请看这篇!往下翻翻就找到了。下文为假做法/不会证明正确性的做法。但是是自己的全流程思路还是记录一下。


考虑把路径的限制转化到 ACAM 上,即把路径看着一个字符串插入 trie 树并建立 ACAM。参考 hdu 4511

考虑这时候在跑 dij 的时候多记一个状态表示当前在 ACAM 上的哪个点,然后 ACAM 上那些路径结尾的点不能走。

建完 ACAM 接着 dij 转移的时候另一维变成 nex 即可。

这时设路径长为 L,不妨设 n,m,L 同阶,则复杂度为 O(n2)。考虑优化。


首先,建 ACAM 时参考 「ACAM 解决字符集很大的情况」,上一个可持久化数组加 unordered_map 即可。

然后考虑 dij 转移,发现大部分情况下 ACAM 上的点唯一对应原图上的点,除了 trie 树的根(其可能对应 n 个点)。

那么我们就可以把原来 O(n2)dis 状态优化到了 O(n)到这里的 record

  • 以上是赛时成果,不仅没判 1 寄了,复杂度其实也是假的。

下文感谢伟大的 Nesraychan 的指导!(其实也是假做法)

注意到一个问题:此时原图上的一个点可能被很多个 ACAM 上的点对应,在遍历其的出边时候就有可能遍历 O(mL) 条边。

ACAM(或理解为 trie 树) 的根为 0 号点。

注意到初始走没路径限制的边一定更优,于是走到 0 号点的边在 vector 中遍历过一次后就可以删去。

注意到 0 号点对应的点很多,比较关键,反正跑很快,而且短小精悍,自信能过(bushi)。讲得有点抽象,具体看代码。

code
#include<bits/stdc++.h>
#define P pair<int,int>
#define fi first
#define se second
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=8e5+5,M=2e7+5;
int n,m,K,tot,cnt,fail[N],bl[N],rt[N],c[N];LL d[N];bool ban[N],v[N];
vector<P>g[N];unordered_map<int,int>nex[N],W[N];
struct node{int x;LL dis;bool operator<(node X)const{return dis>X.dis;}};
priority_queue<node>q;
struct TREE{int ls,rs,x;}tr[M];
void updata(int &wz,int p,int x,int l=1,int r=n)
{
	int wz1=++cnt,mid=(l+r)>>1;tr[wz1]=tr[wz];wz=wz1;
	if(l==r) return tr[wz].x=x,void();
	if(p<=mid) updata(tr[wz].ls,p,x,l,mid);
	else updata(tr[wz].rs,p,x,mid+1,r);
}
int query(int wz,int p,int l=1,int r=n)
{
	if(l==r) return tr[wz].x;int mid=(l+r)>>1;
	return p<=mid?query(tr[wz].ls,p,l,mid):query(tr[wz].rs,p,mid+1,r);
}//可持久化数组
inline void ACAM()
{
	queue<int>q;for(auto [u,v]:nex[0]) updata(rt[0],u,v),q.push(v);
	while(!q.empty())
	{
		int t=q.front(),fl=fail[t];q.pop();rt[t]=rt[fl];
		for(auto [u,v]:nex[t]) q.push(v),fail[v]=query(rt[fl],u),updata(rt[t],u,v);
		ban[t]|=ban[fl];
	}
}//ACAM 建立
inline void dij(int s)
{
	memset(d,0x3f,sizeof(d));int U=query(rt[0],s);
	if(U) q.push({U,d[U]=0});
	else q.push({tot+s,d[tot+s]=0});
	while(!q.empty())
	{
		node t=q.top();q.pop();vector<P>G;
		if(v[t.x]) continue;v[t.x]=1;
		if(bl[t.x]==n){cout<<t.dis;exit(0);}
		for(auto [to,w]:g[bl[t.x]])
		{
			int TO=query(rt[t.x>tot?0:t.x],to),To=TO?TO:tot+to;
			(TO)&&(G.push_back({to,w}),1);//之后把 0 号点 skip 了
			if(ban[TO]) continue;
			(!v[To])&&(q.push({To,t.dis+w}),1);//注意这里一定不能取 min,因为考虑到排除 0 的正确性
		}swap(g[bl[t.x]],G);
	}
}//跑 dij
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m>>K;
	for(int i=1,u,v,w;i<=m;i++) cin>>u>>v>>w,g[u].push_back({v,w});
	for(int i=1,L;i<=K;i++)
	{
		cin>>L;int w=0;
		for(int j=1,x;j<=L;j++)
		{
			cin>>x;
			if(!nex[w].count(x)) nex[w][x]=++tot;
			w=nex[w][x];bl[w]=x;
		}ban[w]=1;
	}ACAM();
	for(int i=1;i<=n;i++) bl[tot+i]=i;dij(1);
	return cout<<-1,0;
}

T2 (soul)#

SourceP9056

你先别急。

T3 (line)#

Sourceqoj 5017

你先别急。


Day3 #

预期得分:[20,40]+50+10=[80,100],实际得分:40+50+0=90rk.26

T1 没认真思考,最后卡卡常多捡了点分。T2 不应该做不出来的。

T1 (fish)#

简要题意#

我们称一个长度为 2k01s 是好的当且仅当:

存在 0x<2k,b{0,1} 满足 0i<2k,si=parity(i and x) xor b

其中 parity 表示一个数字二进制下 1 个数的奇偶性(即:popcountmod2)。

对于一个字符串,你可以进行若干次操作,将它的一个子区间反转,即把 0 变为 11 变为 0

给定一个长度为 n 的字符串 S,进行 q 次询问,每次查询 S[l,r] 需要进行多少次区间反转操作才能变成好的。

要求做到 O(n)O(nlogn)

solution#

注意到修改操作是区间 flip,差分一下。

注意到 parity(x) xor parity(y)=parity(x xor y),于是只需要修改使得 i xor (i1) 相等的位置相等即可,这是容易维护的。

T2 (gardenia)#

SourceDELARR

前置题目高橋君

fn 表示长度为 n 的能被删空的序列,则答案为 12kn(n2k+1)T2k

观察能被删空的条件,容易发现这当且仅当其没有绝对众数。考虑容斥,枚举绝对众数的个数:

T2n=m2nmk=n+12n(2nk)(m1)2nk=m2nmk=0n1(2nk)(m1)k

  • 其中 m 表示绝对众数有 m 种选择方案,然后从 2n 个数种选 k 个绝对众数,剩下数任意填。

a=m1,f(n)=k=0n/2(nk)ak,我们只需递推出 f(0,1,,n) 即可快速计算 T

参考前置题目的方法,有考虑 nn+1 拆组合数。令 d(n+1)=[2n](n+1(n+1)/2)

f(n+1)=d(n+1)+k=0n/2(n+1k)ak=d(n+1)+k=0n/2((nk)+(nk1))ak=d(n+1)+k=0n/2(nk)ak+ak=0n/21(nk)ak=d(n+1)+(a+1)f(n)(nn/2)an/2+1

递推即可,实现精细能做到线性,我写了 O(nlogn),中间用了快速幂。

code
#include<bits/stdc++.h>
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=1e6+5,M=N-5,mod=998244353;
int T,n,m,jc[N],inv[N],f[N];
inline int rd()
{
	int x=0,zf=1;char ch=getchar();
	while(ch<'0'||ch>'9') (ch=='-')and(zf=-1),ch=getchar();
	while(ch>='0'&&ch<='9') x=(10*x+ch-'0'),ch=getchar();
	return x*zf;
}
inline void wr(int x)
{
	if(x==0) return putchar('0'),putchar('\n'),void();
	int num[35],len=0;
	while(x) num[++len]=x%10,x/=10;
	for(int i=len;i>=1;i--) putchar(num[i]+'0');
	putchar('\n');
}
inline int md(int x){return x>=mod?x-mod:x;}
inline int ksm(int x,int p){int s=1;for(;p;(p&1)&&(s=1ll*s*x%mod),x=1ll*x*x%mod,p>>=1);return s;}
inline int C(int n,int m){return n<m?0:1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;}
inline int F(int A,int B){return 1ll*C(A,B)*ksm(m-1,B)%mod;}
int main()
{
	T=rd();for(int i=jc[0]=1;i<=M;i++) jc[i]=1ll*jc[i-1]*i%mod;
	inv[M]=ksm(jc[M],mod-2);for(int i=M-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
	while(T--)
	{
		n=rd(),m=rd();int ans=0;f[0]=1;
		for(int i=0;i<n;i++)
			f[i+1]=((i&1)*F(i+1,(i+2)/2)+f[i]+1ll*(m-1)*(f[i]-F(i,i/2)+mod))%mod;
		for(int i=2;i<=n;i+=2)
			ans=(ans+(ksm(m,i)+1ll*(f[i]-F(i,i/2)+mod)*(mod-m))%mod*(n-i+1)%mod*ksm(m,n-i))%mod;
		wr(ans);
	}
	return 0;
}

T3 (camera)#

简要题意#

交互题。有 n 张卡牌,其中卡牌花色从 1n/2 编号,每种卡牌分别有两张。

你每次可以选择其中两张牌,得知它们的花色分别是什么,然后它们会在随机交换或不交换后放回原位。

你需要在 L 次翻看内确定所有牌的具体花色。

测试点中 (n,L)=(10,100),(103,960),(104,9450),(105,92000)

solution#

你先别急。


Day4 #

100+25+5=130rk.22

T1 (aw)#

SourceCF1770E。改编了。

注:CF 那题与本题有点细节上的不同,但整体思想/思路都是一致的。

简要题意#

  • 2n105,0aw<998244353

solution#

考虑把路径的期望转化为每条边被选中的期望。然后注意到每条边较为独立,然后观察贡献形式,维护一次方和和二次方和就过了。

自己思路太复杂,不想写题解啦!贺一篇。

code
#include<bits/stdc++.h>
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define LL long long
using namespace std;
const int N=1e5+5,mod=998244353;
int n,a[N],b[N],c[N],d[N],S[N],s,ans,pw[N],f[N];
struct node{int u,v;}E[N];
basic_string<int>g[N];
inline int md(int x){return x>=mod?x-mod:x;}
inline void ad(int &x,int y){x=md(x+y);}
inline int ksm(int x,int p){int s=1;for(;p;(p&1)&&(s=1ll*s*x%mod),x=1ll*x*x%mod,p>>=1);return s;}
void dfs(int x,int fa){f[x]=fa;S[x]=a[x];for(int i:g[x]) if(i^fa) dfs(i,x),ad(S[x],S[i]);}
int main()
{
    // fr(aw)
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n;
    for(int i=pw[0]=1;i<=n;i++) cin>>a[i],ad(s,a[i]),pw[i]=md(pw[i-1]<<1);
    for(int i=1,u,v;i<n;i++) cin>>u>>v,E[i]={u,v},g[u]+=v,g[v]+=u;
    dfs(1,0);for(int i=1;i<=n;i++) b[i]=1ll*a[i]*a[i]%mod,c[i]=1;
    for(int i=1;i<n;i++)
    {
        auto [u,v]=E[i];if(f[v]==u) swap(u,v);
        int x=a[u],y=a[v],X=b[u],Y=b[v],S1=S[u],S2=md(s+mod-S1),S3=(1ll*X*c[v]+1ll*Y*c[u])%mod,
        A=1ll*x*c[v]%mod,B=1ll*y*c[u]%mod,C;
        a[u]=a[v]=md(A+B);b[u]=b[v]=(S3+2ll*x*y)%mod;
        C=2ll*c[u]*c[v]%mod;c[u]=c[v]=C;d[u]=d[v]=d[u]+d[v]+1;
        ans=(ans+(mod-S3+1ll*C*S1%mod*S2+1ll*(S1-S2+mod)*(A-B+mod))%mod*pw[n-1-d[u]])%mod;
    }
	return cout<<(ans+mod)%mod,0;
}

CF 那题的 record

T2 (awa)#

简要题意#

T 组多测。给定两个由小写字母组成的字符串 s,t

求有序二元正整数对 (x,y) 的数量,满足 1x,yn,且 s 的长度为 x 的前缀和长度为 y 的后缀拼接得到的字符串是 t 的子串。

这里,拼接表示将后一个字符串拼在前一个字符串之后。

  • 1T10,1|s|,|t|2×105

solution#

你先别急。

T3 (awaw)#

简要题意#

m×m 的网格上有 n 个矩形,第 i 个矩形覆盖了 aixai+w1biybi+h1 的所有格点 (x,y)

求没有被覆盖的格点形成的所有四连通块的大小的平方和。对 264 取模。多测。

注:w,h 全局是固定的。

  • n5×106,1w,hm109,保证输入矩阵不超过边界。

solution#

你先别急。


Day5 #

预期得分:[45,100]+16+20=[81,136],实际得分:75+16+20=111rk.42。菜!

T1 (arithmetic)#

Sourceqoj 7877

对于每个 k,容易发现满足条件的前缀一定形如一个区间 [2k+1,rk],于是问题转化为 k,求 rk

考虑如何判断前缀 [1,l]k 时是否合法,字符串哈希一下,发现转化为 hash(1,l2k)+hash(2k+1,l)=2hash(k+1,lk),预处理哈希值后可 O(1) 判断。

此时直接二分 rk 就可做到 O(nlogn) 了。

考虑从大到小枚举 k 的过程中,记 p=max{r1,r2,rk1},于是此时 <p 的端点就不需要再枚举了,直接从 max(p,2k+1) 开始枚举即可。

预处理下哈希值,端点移动总次数是线性的,于是总复杂度 O(n)

code
//qoj 7877
//https://qoj.ac/problem/7877
#include<bits/stdc++.h>
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define LL long long
#define P pair<int,int>
#define fi first
#define se second
using namespace std;
const int N=2e6+5,mod=1000885909,mod1=1000997923,B=2e8+5;
int n,a[N],b[N],p[N],pw[N],PW[N];bool o[N];P s[N];
inline int to(char c)
{
	if(c>='0'&&c<='9') return c-'0';
	if(c>='a'&&c<='z') return c-'a'+10;
	return c-'A'+36;
}
inline int rd()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'z') ch=getchar();
	while(ch>='0'&&ch<='z') x=x*62+to(ch),ch=getchar();
	return x;
}
inline int md(int x,int mod){return x>=mod?x-mod:x;}
inline P operator*(P x,int y){return {1ll*x.fi*y%mod,1ll*x.se*y%mod1};}
inline P operator<<(P x,int y){return {1ll*x.fi*pw[y]%mod,1ll*x.se*PW[y]%mod1};}
inline P operator+(P x,P y){return {md(x.fi+y.fi,mod),md(x.se+y.se,mod1)};}
inline P operator-(P x,P y){return {md(x.fi-y.fi+mod,mod),md(x.se-y.se+mod1,mod1)};}
inline P S(int l,int r){return s[r]-s[l-1];}
inline bool chk(int l,int k){return (S(1,l-2*k)<<(2*k))+S(2*k+1,l)==((S(k+1,l-k)<<k)*2);}
int main()
{
	scanf("%d",&n);
	for(int i=pw[0]=PW[0]=1;i<=n;i++) a[i]=rd(),pw[i]=1ll*pw[i-1]*B%mod,PW[i]=1ll*PW[i-1]*B%mod1;
	for(int i=1;i<=n;i++) s[i]=s[i-1]+((P){a[i],a[i]}<<i);int r=0;
	for(int k=1;2*k+1<=n;k++)
	{
		for(int i=max(2*k+1,r);i<=n+1;i++)
		{
			if(i<=n&&chk(i,k)) o[i]=1;
			else{r=i-1;break;}
		}
	}
	for(int i=1;i<=n;i++) putchar(o[i]+'0');
	return 0;
}

T2 (cross)#

简要题意#

在平面直角坐标系 Oxy 上有 n 个点 A1,A2,,An

m 次询问,每次询问给出一个点 P,请找到区间 [l,r],使得 i=lrOP×OAi 最大,并输出这个最大值。

注意这里 OP×OAi 是个标量,表示向量叉积的值。

  • 1n105,1m106,所有坐标绝对值不超过 105

solution#

你先别急。

T3 (transform)#

Sourceqoj 1262

你先别急。

Day6 #

理想情况 0+100+10=110T2 没调出来销号了。致敬传奇删号王!

T1 (cormorant)#

SourceP5998

你先别急。

T2 (snowfinch)#

Sourceqoj 888n=2 的弱化版:ARC158E

考虑分治,若 l<r,记 mid 表示 [l,r] 中点,则 一条 (l,o1)(r,o2) 的路径一定跨过 mid

假设我们已经求出了 fi,j,k 表示中点线的第 k(mid/mid+1,k) 到点 (i,j) 的最短路。

那么此时跨过中点的答案:i=lmidj=mid+1ro=02p=02minq=02(fi,o,q+fj,p,q)

发现可以枚举 minp 取多少,然后做 3 次二维偏序算贡献,再加上分治的 log 复杂度为 O(mlog2m)


接下来考虑如何求最短路以及分治到 l=r 的情况。

首先 (x,0)(x,1) 以及 (x,1)(x,2) 的最短路显然是它们俩的权值和。

考虑 (x,0)(x,2) 的情况,发现由于 n=3,于是可能存在如下情况:

写个前缀和然后写个前/后缀 min 即可。

最短路最朴素的想法是直接把 [l,mid] 的点拎出来跑以 (mid,0/1/2) 为源点的单源最短路,但发现可能存在如下更优情况:

处理方式也很简单,只需要在最左侧加上一条 (l,0)(l,2) 且边权为其更左侧最优的 w 的边,然后跑 dij 即可。

右侧同理,w 是容易通过之前预处理的前/后缀 min 得到,就做完了,dij 复杂度和偏序一致,于是复杂度不变,为 O(mlog2m)

code
//qoj 888
//https://qoj.ac/contest/532/problem/888
#include<bits/stdc++.h>
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define LL long long
using namespace std;
const int N=2e5+5,M=N*3,mod=1e9+7,dx[]={1,-1,0,0},dy[]={0,0,1,-1};
int n,ans,tot,tot1,cnt;bool v[N][3];
LL a[N][3],f[N][3],g[N][3],h[N][3],d[M<<1],s1[N],s2[N],F[N],G[N],D[N][3],mn[N],MN[N];
struct node{LL x,y;int w;}b[M],c[M];
inline bool cmp(node X,node Y){return X.x<Y.x;}
inline int md(int x){return x>=mod?x-mod:x;}
inline void ad(int &x,int y){x=md(x+y);}
struct Bit
{
	int a[M],b[M];
	inline int lb(int x){return x&-x;}
	inline void add(int wz,int x,int y){for(;wz<=cnt;wz+=lb(wz)) a[wz]+=x,ad(b[wz],y);}
	inline int ask1(int wz){int ans=0;for(;wz;wz-=lb(wz)) ans+=a[wz];return ans;}
	inline int ask2(int wz){int ans=0;for(;wz;wz-=lb(wz)) ad(ans,b[wz]);return ans;}
}BIT;
inline void calc()
{
	cnt=0;int t1=1,t2=1;
	for(int i=1;i<=tot;i++) d[++cnt]=b[i].y;
	for(int i=1;i<=tot1;i++) d[++cnt]=c[i].y;
	sort(d+1,d+1+cnt);cnt=unique(d+1,d+1+cnt)-d-1;
	#define ls(x) lower_bound(d+1,d+1+cnt,x)-d
	for(int i=1;i<=tot;i++) b[i].y=ls(b[i].y);
	for(int i=1;i<=tot1;i++) c[i].y=ls(c[i].y);
	sort(b+1,b+1+tot,cmp);sort(c+1,c+1+tot1,cmp);
	for(;t2<=tot;t2++)
	{
		while(c[t1].x<=b[t2].x&&t1<=tot1) BIT.add(c[t1].y,1,c[t1].w),t1++;
		ans=(ans+1ll*b[t2].w*BIT.ask1(b[t2].y)+BIT.ask2(b[t2].y))%mod;
	}
	for(t1=t2=1;t2<=tot;t2++)
		while(c[t1].x<=b[t2].x&&t1<=tot1) BIT.add(c[t1].y,-1,mod-c[t1].w),t1++;
}//二维数点
struct Node{int x,y;LL dis;bool operator<(Node X)const{return dis>X.dis;}};
priority_queue<Node>q;
int L,R;
inline void dij(int l,int r,int U,int V)
{
	for(int i=l;i<=r;i++) for(int j=0;j<3;j++) D[i][j]=1e18,v[i][j]=0;
	q.push({U,V,D[U][V]=a[U][V]});
	while(!q.empty())
	{
		auto [x,y,dis]=q.top();q.pop();
		if(v[x][y]) continue;v[x][y]=1;
		for(int k=0;k<4;k++)
		{
			int nx=x+dx[k],ny=y+dy[k];
			if(l<=nx&&nx<=r&&0<=ny&&ny<3&&D[nx][ny]>dis+a[nx][ny])
				D[nx][ny]=dis+a[nx][ny],(!v[nx][ny])&&(q.push({nx,ny,D[nx][ny]}),1);
		}//一般情况的转移
		if((x==L||x==R)&&y!=1)
		{
			int nx=x,ny=2-y;LL w=((x==L)?MN[x]:mn[x])-a[x][y];
			if(D[nx][ny]>dis+w)
				D[nx][ny]=dis+w,(!v[nx][ny])&&(q.push({nx,ny,D[nx][ny]}),1);
		}//处理最左侧和最右侧的 Corner Case
	}
}//dij
void sol(int l,int r)
{
	if(l==r)
	{
		ans=(ans+a[l][0]+2ll*a[l][1]+a[l][2]+min(mn[l],MN[l]))%mod;
		return;
	}int mid=(l+r)>>1;sol(l,mid);sol(mid+1,r);L=l,R=r;
	dij(l,mid,mid,0);
	for(int i=l;i<=mid;i++) for(int j=0;j<3;j++) f[i][j]=D[i][j];
	dij(mid+1,r,mid+1,0);
	for(int i=mid+1;i<=r;i++) for(int j=0;j<3;j++) f[i][j]=D[i][j];
	dij(l,mid,mid,1);
	for(int i=l;i<=mid;i++) for(int j=0;j<3;j++) g[i][j]=D[i][j];
	dij(mid+1,r,mid+1,1);
	for(int i=mid+1;i<=r;i++) for(int j=0;j<3;j++) g[i][j]=D[i][j];
	dij(l,mid,mid,2);
	for(int i=l;i<=mid;i++) for(int j=0;j<3;j++) h[i][j]=D[i][j];
	dij(mid+1,r,mid+1,2);
	for(int i=mid+1;i<=r;i++) for(int j=0;j<3;j++) h[i][j]=D[i][j];//f,g,h 分别表示 f_{xx,xx,0/1/2}
	tot=tot1=0;
	for(int i=l;i<=mid;i++) for(int j=0;j<3;j++)
		b[++tot]={g[i][j]-f[i][j],h[i][j]-f[i][j],(int)(f[i][j]%mod)};
	for(int i=mid+1;i<=r;i++) for(int j=0;j<3;j++)
		c[++tot1]={f[i][j]-g[i][j],f[i][j]-h[i][j],(int)(f[i][j]%mod)};//枚举 min 在哪个,写出偏序式子
	calc();//偏序
	tot=tot1=0;
	for(int i=l;i<=mid;i++) for(int j=0;j<3;j++)
		b[++tot]={f[i][j]-g[i][j],h[i][j]-g[i][j],(int)(g[i][j]%mod)};
	for(int i=mid+1;i<=r;i++) for(int j=0;j<3;j++)
		c[++tot1]={g[i][j]-f[i][j]+1,g[i][j]-h[i][j],(int)(g[i][j]%mod)};
	calc();
	tot=tot1=0;
	for(int i=l;i<=mid;i++) for(int j=0;j<3;j++)
		b[++tot]={f[i][j]-h[i][j],g[i][j]-h[i][j],(int)(h[i][j]%mod)};
	for(int i=mid+1;i<=r;i++) for(int j=0;j<3;j++)
		c[++tot1]={h[i][j]-f[i][j]+1,h[i][j]-g[i][j]+1,(int)(h[i][j]%mod)};
	calc();//分三类讨论
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>n;
	for(int i=0;i<3;i++) for(int j=1;j<=n;j++) cin>>a[j][i];
	for(int i=1;i<=n;i++) s1[i]=s1[i-1]+a[i][0],s2[i]=s2[i-1]+a[i][2];
	F[n+1]=1e18;for(int i=n;i;i--) F[i]=min(F[i+1],s1[i]+s2[i]+a[i][1]);
	G[0]=-1e18;for(int i=1;i<=n;i++) G[i]=max(G[i-1],s1[i-1]+s2[i-1]-a[i][1]);//记前后缀 min,这里 G 取相反数所以是 max
	for(int i=1;i<=n;i++) mn[i]=F[i]-s1[i-1]-s2[i-1],MN[i]=s1[i]+s2[i]-G[i];
	sol(1,n);//分治
	return cout<<(2ll*ans%mod),0;//每个点对要贡献两次答案
}

T3 (tern)#

Source高橋くんの帰還

你先别急。

Day7 #

预期得分 100+15+[?]=115+[?],实际得分 100+5+5=110rk.37

T1 (densha)#

简要题意#

给定 n,计算有多少个 1n 的排列满足积性函数性质。即 1i,j,ijn,aij=aiaj

T 组多测,对 998244353 取模。

  • T104,1n107

solution#

容易通过打表找规律/打表 oeis/代数推导发现:另 Sd={p:pn,pPrime,n/p=d},则 ans=d(|Sd|!)

线性筛出素数后再整除分块计算即可,复杂度 O(n+Tn)

注:此数列在 OEIS 上为 A357328

code
#include<bits/stdc++.h>
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define LL long long
using namespace std;
const int N=1e7+5,mod=998244353;
int T,n,pr[N/10],jc[N],s[N];bool v[N];
inline void init(int M)
{
	for(int i=2;i<=M;i++)
	{
		if(!v[i]) pr[++pr[0]]=i;
		for(int j=1;j<=pr[0]&&i*pr[j]<=M;j++)
		{
			v[i*pr[j]]=1;
			if(i%pr[j]==0) break;
		}
	}
    for(int i=jc[0]=1;i<=M;i++) jc[i]=1ll*jc[i-1]*i%mod;
    for(int i=1;i<=pr[0];i++) s[pr[i]]++;
    for(int i=1;i<=M;i++) s[i]+=s[i-1];
}
int main()
{
    // fr(densha)
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>T;init(N-5);
    while(T--)
    {
        cin>>n;int ans=1;
        for(int i=1,j;i<=n;i=j+1) j=n/(n/i),ans=1ll*ans*jc[s[j]-s[i-1]]%mod;
        cout<<ans<<"\n";
    }
	return 0;
}

T2 (hacho)#

简要题意#

对于一个序列 a,可以对其进行若干次操作,每次将一个数减少 1

定义 fk 表示进行 k 次操作后序列的最大子段和最小能达到多少。

给定 a,K,计算 k=1Kfk,答案对 998244353 取模。

  • 1n105,1K1013,|ai|108

solution#

你先别急。

T3 (hokaku)#

SourceAT wtf22 day2 c

你先别急。

posted @   HaHeHyt  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩
主题色彩