模拟赛D1

T 1 T1

很套路的矩阵乘法,但是考试的时候没有想到…

定义 f [ i ] [ j ] f[i][j] 表示前 i i j j 个格子不选.

{ f [ i ] [ 0 ] = f [ i 1 ] [ 0 ] + f [ i 2 ] [ 0 ] f [ i ] [ 1 ] = f [ i 1 ] [ 1 ] ] + 2 f [ i 1 ] [ 0 ] f [ i ] [ 2 ] = f [ i 1 ] [ 2 ] + f [ i 2 ] [ 2 ] + f [ i 2 ] [ 1 ] \begin{cases} f[i][0]=f[i-1][0]+f[i-2][0]\\ f[i][1]=f[i-1][1]]+2f[i-1][0]\\ f[i][2]=f[i-1][2]+f[i-2][2]+f[i-2][1]\end{cases}

  1. 斐波那契数列
  2. i i 列选择有两种方案
  3. i i 列选择的话就只有一种方案且与前一个放置位置的列的差至少为2.

我们可以精简一下式子:

{ f [ i ] [ 0 ] = f i b i f [ i ] [ 1 ] = 2 j = 1 i 1 f i b j f [ i ] [ 2 ] = f [ i 1 ] [ 2 ] + f [ i 2 ] [ 2 ] + j = 1 i 3 f i b j \begin{cases} f[i][0]=fib_i\\ f[i][1]=2\sum_{j=1}^{i-1} fib_j\\ f[i][2]=f[i-1][2]+f[i-2][2]+\sum_{j=1}^{i-3} fib_j\end{cases}

f [ i ] = f [ i ] [ 2 ] f[i]=f[i][2] ,则 f [ i ] = f [ i 1 ] + f [ i ] + 2 j = 1 i = 3 f i b j = f [ i 1 ] + f [ i ] + 2 ( f i b i 1 1 ) f[i]=f[i-1]+f[i]+2\sum_{j=1}^{i=3}fib_j=f[i-1]+f[i]+2(fib_{i-1}-1)

这里利用了一个性质: i = 1 n f i b i = f i b n + 2 1 \sum_{i=1}^n fib_i=fib_{n+2}-1 .(用数学归纳法可以证明)

所以我们定义一个5维向量 [ f [ i ] , f [ i 1 ] , f i b [ i ] , f i b [ i 1 ] , 2 ] [f[i],f[i-1],fib[i],fib[i-1],-2] ,用矩阵乘法优化一下即可.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar()
using namespace std;
typedef long long ll;
const int N=5,mod=1e9+7;
const ll inf=1e18;

template<class o> void qr(o &x) {
	char c=gc; x=0; int f=1;
	while(!isdigit(c)){if(c=='-') f=-1; c=gc;}
	while(isdigit(c))x=x*10+c-'0',c=gc;
	x*=f;
}
template<class o> void qw(o x) {
	if(x<0) putchar('-'),x=-x;
	if(x/10) qw(x/10);
	putchar(x%10+'0');
}
template<class o> void pr1(o x) {qw(x); putchar(' ');}
template<class o> void pr2(o x) {qw(x); puts("");}


int T;
ll ans,n;

struct rec {
	ll a[N][N];
	rec() {memset(a,0,sizeof(a));}
	rec operator *(rec b) const {
		rec c;
		for(int i=0;i<N;i++)
			for(int j=0;j<N;j++) if(a[i][j])
				for(int k=0;k<N;k++) 
					c.a[i][k]+=a[i][j]*b.a[j][k]%mod;
		for(int i=0;i<N;i++)
			for(int j=0;j<N;j++) c.a[i][j]%=mod;
		return c;
	}
} f,g;

int main() {
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	qr(T); while(T--) {
		qr(n); f=rec(); g=rec(); 
		f.a[0][2]=1; f.a[0][4]=mod-2;
		g.a[0][0]=1; g.a[0][1]=1; 
		g.a[1][0]=1; 
		g.a[2][0]=2; g.a[2][2]=1; g.a[2][3]=1;
		g.a[3][2]=1;
		g.a[4][0]=g.a[4][4]=1;
		while(n) {
			if(n&1) f=f*g;
			n /= 2; g=g*g;
		}
		pr2(f.a[0][0]);
	}
	return 0;
}


T 2 T2

正反向跑最短路.

枚举每条边 ( x , y ) (x,y) ,然后如果 X , Y X,Y 的来源( A , B A,B )不同,那么就合法.

为啥一定会遍历到最优解:

A B A\rightarrow B 的路径上,

有一部分点在正向图中由 A A 得到,有一部分点在反向图中由 B B 得到,

而且显然至少存在一条边沟通这两个点集.

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar()
#define pi pair<ll,int>
#define mk make_pair
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+10,M=1e6+10;
const ll inf=1e18;

template<class o> void qr(o &x) {
	char c=gc; x=0; int f=1;
	while(!isdigit(c)){if(c=='-') f=-1; c=gc;}
	while(isdigit(c))x=x*10+c-'0',c=gc;
	x*=f;
}
template<class o> void qw(o x) {
	if(x<0) putchar('-'),x=-x;
	if(x/10) qw(x/10);
	putchar(x%10+'0');
}
template<class o> void pr1(o x) {qw(x); putchar(' ');}
template<class o> void pr2(o x) {qw(x); puts("");}

int T,n,m,t,b[N],st[2][N];
ll d[2][N],ans;
struct edge{int y,next; ll d;}a[M]; int len,last[N];
struct rec{int x,y,z;} tmp[M];
void ins(int x,int y,ll d) {a[++len]=(edge){y,last[x],d}; last[x]=len; }

void dijkstra(int f) {
	priority_queue<pi> q;
	memset(d[f]+1,63,sizeof(ll[n]));
	for(int i=1;i<=t;i++) d[f][b[i]]=0,st[f][b[i]]=b[i],q.push(mk(0,b[i]));
	memset(last+1,0,sizeof(int[n])); len=0;
	if(f) for(int i=1;i<=m;i++) ins(tmp[i].y,tmp[i].x,tmp[i].z);
	else  for(int i=1;i<=m;i++) ins(tmp[i].x,tmp[i].y,tmp[i].z);
	while(!q.empty()) {
		int x=q.top().second;
		ll D=-q.top().first; q.pop();
		if(d[f][x]^D) continue;
		for(int k=last[x],y;k;k=a[k].next) {
			y=a[k].y;
			if(d[f][x]+a[k].d<d[f][y]) {
				d[f][y]=d[f][x]+a[k].d;
				q.push(mk(-d[f][y],y));
				st[f][y]=st[f][x];
			}
		}
	}
}

int main() {
	qr(T); while(T--) {
		qr(n); qr(m); qr(t);
		for(int i=1;i<=m;i++) qr(tmp[i].x),qr(tmp[i].y),qr(tmp[i].z);
		for(int i=1;i<=t;i++) qr(b[i]);
		ans=inf; 
		dijkstra(0); dijkstra(1);//0正向,1反向
		for(int i=1;i<=m;i++)
			if(st[0][tmp[i].x]^st[1][tmp[i].y]) 
				ans=min(ans,d[0][tmp[i].x]+d[1][tmp[i].y]+tmp[i].z);
		pr2(ans); 
	}
	return 0;
}


T 3 T3

考试的时候只想出了 k = 1 k=1 的情况,其实再仔细想想即可得到正解.

树剖中线段树维护差分权值*被覆盖次数之和即可.

#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar()
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=5e4+10,M=N,mod=998244353;
const ll inf=1e18;

template<class o> void qr(o &x) {
	char c=gc; x=0; int f=1;
	while(!isdigit(c)){if(c=='-') f=-1; c=gc;}
	while(isdigit(c))x=x*10+c-'0',c=gc;
	x*=f;
}
template<class o> void qw(o x) {
	if(x<0) putchar('-'),x=-x;
	if(x/10) qw(x/10);
	putchar(x%10+'0');
}
template<class o> void pr1(o x) {qw(x); putchar(' ');}
template<class o> void pr2(o x) {qw(x); puts("");}

int n,m,t,fa[N],p[N],dep[N],sz[N],son[N],mx,f[N],d[N];
struct edge{int y,next;}a[N]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len;}

ll power(ll a,ll b=t) {
	ll c=1;
	while(b) {
		if(b&1) c=c*a%mod;
		b /= 2; a=a*a%mod; 
	}
	return c;
}

template<class o>void upd(o &x) {x+=x>>31&mod; if(x>=mod) x-=mod; }

struct rec {
	int x,y,id;
	bool operator <(rec b) const {
		return x<b.x;
	}
} q[M]; int ans[M];

int sta[N],top;

void dfs(int x) {
	son[x]=0; sz[x]=1;
	for(int k=last[x],y;k;k=a[k].next) {
		y=a[k].y; dep[y]=dep[x]+1; dfs(y);
		if(sz[son[x]]<sz[y]) son[x]=y;
		sz[x]+=sz[y];
	}
}

//树剖
int id[N],z,b[N],tp[N];
void DFS(int x,int T) {
	tp[x]=T; id[x]=++z; upd(b[z]=b[z-1]+d[dep[x]]);
	if(son[x]) DFS(son[x],T);
	for(int k=last[x],y;k;k=a[k].next)
		if((y=a[k].y)^son[x]) DFS(y,y);
}

int s[N<<2];//权*次数之和
int ad[N<<2];//懒标记
void upd(int x,int l,int r,int c) {upd(s[x]+=(ll)(b[r]-b[l-1]+mod)*c%mod);}
void add(int x,int l,int r,int L,int R) {
	if(L<=l&&r<=R) {upd(x,l,r,1); ad[x]++; return ;}
	int mid=(l+r)>>1;
	if(ad[x]) {
		upd(lc,l,mid,ad[x]);
		ad[lc]+=ad[x];
		upd(rc,mid+1,r,ad[x]);
		ad[rc]+=ad[x];
		ad[x]=0;
	}
	if(L<=mid) add(lc,l,mid,L,R);
	if(mid< R) add(rc,mid+1,r,L,R);
	upd(s[x]=s[lc]+s[rc]);
}

int ask(int x,int l,int r,int L,int R) {
	if(L<=l&&r<=R) return s[x];
	int mid=(l+r)>>1;
	if(ad[x]) {
		upd(lc,l,mid,ad[x]);
		ad[lc]+=ad[x];
		upd(rc,mid+1,r,ad[x]);
		ad[rc]+=ad[x];
		ad[x]=0;
	}
	return ((L<=mid?ask(lc,l,mid,L,R):0LL)+
		   (mid<R?ask(rc,mid+1,r,L,R):0LL))%mod;
}

void add(int x) {
	while(x) {
		int y=tp[x];
		add(1,1,n,id[y],id[x]);
		x=fa[y];
	}
}

int solve(int x) {
	int y; ll s=0;
	while(x) {
		y=tp[x];
		s+=ask(1,1,n,id[y],id[x]);
		x=fa[y];
	}
	return s%mod;
}


int main() {
//	freopen("c.in","r",stdin);
//	freopen("c.out","w",stdout);
	qr(n); qr(m); qr(t);
	for(int i=1;i<=n;i++) p[i]=power(i),upd(d[i]=p[i]-p[i-1]);
	for(int i=2;i<=n;i++) qr(fa[i]),ins(fa[i],i);
	for(int i=1;i<=m;i++) qr(q[i].x),qr(q[i].y),q[i].id=i;
	sort(q+1,q+m+1); dep[1]=1; dfs(1); DFS(1,1);
	for(int i=1,p=1;i<=n;i++) {
		add(i);
		while(q[p].x==i)
			ans[q[p].id]=solve(q[p].y),p++;
	}
	for(int i=1;i<=m;i++) pr2(ans[i]);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/106623461
d1
今日推荐