Loading

2022.7.30 模拟赛

2022.7.30 模拟赛

\(\to link\leftarrow\)

序列

看到大的一直减去小的,很容易想到辗转相减法\(\gcd\) 的公式:

\[\gcd(a,b)=\gcd(a,a-b)\ \ (a\ge b) \]

显然我们可以把问题转变成:变成所有非最小值辗转相除最小值 (模仿求 \(\gcd\) 的过程)

由于单次辗转相除求 \(\gcd\)\(\log a\) 的 (\(a\) 表示最大数)

那么显然对整个序列求一遍时间复杂度就是 \(\text O(n\log a)\)

当然这个过程实际上相当于求一边所有数的 \(\gcd\)

所以我为什么没想到这一点而是写了更长的代码

code

#include<bits/stdc++.h>
using namespace std;

#define ll long long

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n;
int a[100005];

inline int solve(int &x){
	for(int i=x-1;i>=1;--i) a[i]=a[i]%a[x];
	for(int i=x+1;i<=n;++i) a[i]=a[i]%a[x];
	int mn=1e9+1,cnt=0;
	for(int i=1;i<=n;++i){
		if(!a[i]) continue;
		++cnt;
		if(mn>a[i]){
			mn=a[i];
			x=i;
		}
	}
	return cnt>1;
}

signed main(){
	n=read();
	int mn=1e9+1,id;
	for(int i=1;i<=n;++i){
		a[i]=read();
		if(a[i]<mn){
			mn=a[i];
			id=i;
		}
	}
	while(1){
		if(!solve(id)) break;
	}
	printf("%d\n",a[id]);
}

任意模数快速插值

code

#include <bits/stdc++.h>
using namespace std;
#define lsp p<<1
#define rsp p<<1|1
#define add(x,y) ({int xx=x+y;xx<M?xx:xx-M;})

const int N=5e5+5,M=998244353;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n,ans;

struct Tree{
   int s,d1,x1,d2,x2,s1,s2,t1,t2;
}t[N*4];

inline void Update1(int p,int l,int r,int w){
    t[p].d1=t[p].x1=t[p].t1=w;
    t[p].s1=1ll*w*(r-l+1)%M;
    t[p].s=1ll*w*t[p].s2%M;
}

inline void Update2(int p,int l,int r,int w){
    t[p].d2=t[p].x2=t[p].t2=w;
    t[p].s2=1ll*w*(r-l+1)%M;
    t[p].s=1ll*w*t[p].s1%M;
}

inline void push_down(int p,int l,int r){
   	int mid=l+r>>1;
    if(t[p].t1!=-1){
        Update1(lsp,l,mid,t[p].t1);
        Update1(rsp,mid+1,r,t[p].t1);
    }
    if(t[p].t2!=-1){
        Update2(lsp,l,mid,t[p].t2);
        Update2(rsp,mid+1,r,t[p].t2);
    }
    t[p].t1=t[p].t2=-1;
}

inline void update1(int p,int l,int r,int L,int R,int w){
    if(t[p].x1>=w) return;
    if(L<=l&&r<=R&&t[p].d1<w) return Update1(p,l,r,w);
    int mid=l+r>>1;
    push_down(p,l,r);
    if(L<=mid) update1(lsp,l,mid,L,R,w);
    if(R>mid) update1(rsp,mid+1,r,L,R,w);
    t[p].d1=max(t[lsp].d1,t[rsp].d1);
    t[p].x1=min(t[lsp].x1,t[rsp].x1);
    t[p].s1=add(t[lsp].s1,t[rsp].s1);
    t[p].s=add(t[lsp].s,t[rsp].s);
}

inline void update2(int p,int l,int r,int x,int y,int w){
    if(t[p].d2<=w) return;
    if(x<=l&&r<=y&&t[p].x2>w) return Update2(p,l,r,w);
   int mid=l+r>>1;push_down(p,l,r);
    if(x<=mid) update2(lsp,l,mid,x,y,w);
    if(y>mid) update2(rsp,mid+1,r,x,y,w);
    t[p].d2=max(t[lsp].d2,t[rsp].d2);
    t[p].x2=min(t[lsp].x2,t[rsp].x2);
    t[p].s2=add(t[lsp].s2,t[rsp].s2);
    t[p].s=add(t[lsp].s,t[rsp].s);
}

inline int query(int p,int l,int r,int L,int R){
   	if(L<=l&&r<=R) return t[p].s;
   	int mid=l+r>>1,res=0;
   	push_down(p,l,r);
    if(L<=mid) res=query(lsp,l,mid,L,R);
    if(R>mid) res=add(res,query(rsp,mid+1,r,L,R));
    return res;
}

signed main(){
    n=read();
    for (int i=n*4;i>=1;--i) 
        t[i].d2=t[i].x2=1e9,t[i].t1=t[i].t2=-1;
    for (int i=1;i<=n;++i){
       int x=read();
        update1(1,1,n,1,i,x);
        update2(1,1,n,1,i,x);
        if((ans+=query(1,1,n,1,i))>=M) ans-=M;
    }
    printf("%d\n",ans);
    return 0;
}

快递

\(1\le n\le 20,1\le q\le 10\)

第一想法网络流,但是好像不可做

由于 \(q\) 很小,想的是状压

但是发现自己想不出 \(dp\) 的顺序,于是干脆不想了,写的状压记忆化搜索

先跑一遍 \(floyd\) 求出两两之间的最短路 \(g\)

这里设的 \(f_{i,j,k}\) 表示到达 \(i\),当前手里的快递为 \(j\),已经送完的快递为 \(k\) 的最小时间

那么有 (条件没写):

\[f_{x,st,ed}\to f_{s[i],st|(1<<i),ed}\\ f_{x,st,ed}\to f_{t[i],st^(1<<i),ed|(1<<i)} \]

第一个转移表示拿,第二个转移表示送

还有一些细节看代码

code

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int N=21;
const int inf=1e9;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n,m,q,ans,mx;
int g[N][N];
int f[N][1<<10][1<<10];
int s[N],t[N],l[N],r[N];
int c[1<<10];

inline void floyd(){
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j){
			if(i==j) continue;
			for(int k=1;k<=n;++k){
				if(i==k||j==k) continue;
				g[j][k]=min(g[j][k],g[j][i]+g[i][k]);
			}
		}
}
inline void calc(int x){
	for(int i=0;i<q;++i)
		c[x]+=(x&(1<<i))!=0;
}
inline void dfs(int x,int st,int ed,int T){
	//cout<<x<<" "<<st<<" "<<ed<<" "<<T<<endl;
	if(f[x][st][ed]<=T||T>mx) return;
	f[x][st][ed]=T;
	ans=max(ans,c[ed]);
	for(int i=0;i<q;++i){
		if((st&(1<<i))||(ed&(1<<i))) continue;
		dfs(s[i],st|(1<<i),ed,max(l[i],T+g[x][s[i]]));
	}
	for(int i=0;i<q;++i){
		if(((st&(1<<i))==0)||T+g[x][t[i]]>r[i]) continue;
		dfs(t[i],st^(1<<i),ed|(1<<i),T+g[x][t[i]]);
	}
}

signed main(){
	n=read(),m=read(),q=read();
	for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) g[i][j]=inf;
	for(int i=1;i<=n;++i) g[i][i]=0;
	for(int i=1;i<=n;++i) 
		for(int j=0;j<(1<<10);++j) 
			for(int k=0;k<(1<<10);++k) 
				f[i][j][k]=inf;
	for(int i=0;i<(1<<10);++i) calc(i);
	while(m--){
		int x=read(),y=read(),z=read();
		g[x][y]=min(g[x][y],z);
	}
	floyd();
	for(int i=0;i<q;++i){
		s[i]=read(),t[i]=read(),l[i]=read(),r[i]=read();
		mx=max(mx,r[i]);
	}
	dfs(1,0,0,0);
	printf("%d\n",ans);
}

任意模数多项式乘法逆

神仙题,赛时无人 \(\text{AC}\)

然而我也不会啊 \(qaq\)

这是题解

posted @ 2022-07-30 14:07  Into_qwq  阅读(58)  评论(0编辑  收藏  举报