2023.6.4拷逝

T1

首先题目没有强制让我们一起算 \(k^{r(p)}+r^2(p)\) ,我们可以把它拆成两部分,一部分是 \(k^{r(p)}\) ,一部分是 \(r^2(p)\)

考虑递推求解两个部分。先看第一个部分。设 \(n\) 的全排列的逆序对个数分别是 \(p_1,p_2,...,p_{n!}\) ,并假设我们已经知道 \(k^{r(p)}\) 的值。现在新增一个数 \(n+1\) ,如果它是最后一个数,那么新形成的排列的逆序对个数分别是 \(p_1,p_2,...,p_{n!}\) ;如果它是倒第二个数,那么新形成的排列逆序对个数分别是 \(p_1+1,p_2+1,...,p_{n!}+1\) ......如果它是第一个数,那么新形成的排列的逆序对个数分别是 \(p_1+n,p_2+n,...,p_{n!}+n\) 。这时新的 \(\sum k^{r(p')}\) 的结果应该是 \(\sum k^{r(p)}\times (1+k+k^2+...+k^n)\)

再看第二个部分。新的 \(\sum r^2(p')\) 应该是 \(\sum r^2(p)\times (n+1)+(2+4+...+2n)\times \sum r(p)+n!\times (1^2+2^2+3^2+...+n^2)\) 。但这时我们引入了 \(\sum r(p')\) ,还需要同时更新它。 \(\sum r(p')=(1+2+...+n)\times n!+\sum r(p)\times (n+1)\) .

处理好这两部分后,直接相加就是答案。时间复杂度\(O(n+t)\) .

\(code:\)

#include<iostream>
#include<cstdio>
using namespace std;
const long long mod=1e9+7;
long long n,t,k,fac,ans1[5],ans2[5],ans3[5];
int main(){
	//freopen("concert.in","r",stdin);
	//freopen("concert.out","w",stdout);
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&k);
		ans1[1]=1;ans2[1]=ans3[1]=0;//1:k^n,2:n^2,3:n
		long long sum=1,sum2=0,p=1;
		fac=1;
		for(long long i=2;i<=n;++i){
			p=p*k%mod;
			sum=(sum+p)%mod;
			ans1[i&1]=(ans1[(i-1)&1]*sum)%mod;
		}
		for(long long i=2;i<=n;++i){
			fac=fac*(i-1)%mod;
			sum2=(sum2+(i-1)*(i-1)%mod)%mod;
			ans3[i&1]=(fac*(i*(i-1)/2%mod)%mod+(i*ans3[(i-1)&1]%mod))%mod;
			ans2[i&1]=ans2[(i-1)&1]*i%mod;
			ans2[i&1]=(ans2[i&1]+((ans3[(i-1)&1]*i%mod*(i-1)%mod)%mod))%mod;
			ans2[i&1]=(ans2[i&1]+(fac*sum2%mod))%mod;
		}
		printf("%lld\n",(ans1[n&1]+ans2[n&1])%mod);
	}
	fclose(stdin);fclose(stdout);
	return 0;
}

T2

新建一个超级源点,将超级源点和所有朋友连边,跑最短路,输出每个点的路径长度\(-1\)即可。

\(code:\)

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int l=100005;
int n,m,k,u,v,tot,pos[l],nxt[l*20],ver[l*20],head[l*20],ans[l],vis[l],dis[l];
struct node{
	int to,w;
	friend bool operator < (node a,node b){
		return a.w>b.w;
	};
};priority_queue <node> q;
void add(int x,int y){
	nxt[++tot]=head[x];head[x]=tot;ver[tot]=y;
} 
void bfs(int x){
	q.push((node){x,0});dis[x]=0;
	while(!q.empty()){
		node f=q.top();q.pop();
		if(vis[f.to])
			continue;
		vis[f.to]=1;
		for(int i=head[f.to];i;i=nxt[i]){
			int y=ver[i];
			if(dis[y]>dis[f.to]+1){
				dis[y]=1+dis[f.to];
				q.push((node){y,dis[y]}); 
			}
		}
	}
}
int main(){
	//freopen("39c5bb.in","r",stdin);
	//freopen("39c5bb.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;++i){
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	for(int i=1;i<=n;++i)
		ans[i]=1e9;
	bool ok=1;
	for(int i=1;i<=k;++i)
		scanf("%d",&pos[i]),add(0,pos[i]),add(pos[i],0);
	for(int i=0;i<=n;++i)
		vis[i]=0,dis[i]=1e9;
	bfs(0);
	for(int i=1;i<=n;++i)
		printf("%d ",dis[i]-1);
	printf("\n"); 
	fclose(stdin);fclose(stdout);
	return 0;
} 

T3

先考虑从起点走到某个位置再乘车的情况。如果人很快就走到了,那么得等车到了才能乘车,和直接从起点乘车没有区别;如果车到了,人还没走到,那么从起点乘车是更优的。综上,我们发现,从起点到终点的最优方案要么是先乘车,后走路,要么是只乘车。

根据题意,显然有从起点乘车到点 \(s[i]\) 下车的距离 \(i-1\) ,然后加上点 \(s[i]\) 到终点的最短路,就是总距离。所以,我们可以建一个反图,从终点开始跑每个点的最短路。答案就是\(min\{i-1+dis[s[i]]\}\),其中 \(dis[s[i]]\) 表示终点到点 \(s[i]\) 的最短路。

那么如何考虑修改呢?可以建立一颗线段树。线段树中,代表区间 \([l,r]\) 的节点的权值为点 \(l\) 到点 \(r\) 中的某个点为下车点,起点到终点距离的最小值。如果要交换 \(s[x],s[y]\) ,那么把点 \(s[x]\) 的权值加上 \(y-x\) ,并且把点 \(s[y]\) 的权值加上 \(x-y\) .输出答案时输出根节点的权值即可。

\(code:\)

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int ans,tot,n,m,d,a,b,s[200005],x,y,u,v,w[200005];
int head[400005],nxt[400005],ver[400005],vis[200005],dis[200005];
queue <int> q;
struct node{
	int l,r,w;
} p[2000005];
void add(int x,int y){
	nxt[++tot]=head[x];head[x]=tot;ver[tot]=y;
} 
void bfs(int x){
	for(int i=1;i<=n;++i)
		dis[i]=1e9,vis[i]=0;
	q.push(x);dis[x]=0;
	while(!q.empty()){
		int f=q.front();q.pop();
		if(vis[f])
			continue;
		vis[f]=1;
		for(int i=head[f];i;i=nxt[i]){
			int y=ver[i];
			if(dis[y]>dis[f]+1){
				dis[y]=1+dis[f];
				q.push(y); 
			}
		}
	}
}
void build(int u,int l,int r){
	p[u].l=l;p[u].r=r;
	if(l==r){
		p[u].w=w[r];return ;
	}
	int mid=(l+r)>>1;
	build(u<<1,l,mid);build(u<<1|1,mid+1,r);
	p[u].w=min(p[u<<1].w,p[u<<1|1].w);
}
void add(int u,int pos,int w){
	if(p[u].l==pos&&p[u].r==pos){
		p[u].w+=w;return ;
	}
	int mid=(p[u].l+p[u].r)>>1;
	if(mid>=pos)
		add(u<<1,pos,w);
	if(mid<pos)
		add(u<<1|1,pos,w);
	p[u].w=min(p[u<<1].w,p[u<<1|1].w);
}
int main(){
	//freopen("paincar.in","r",stdin);
	//freopen("paincar.out","w",stdout);
	scanf("%d%d%d",&n,&m,&d);
	for(int i=1;i<=m;++i)
		scanf("%d%d",&a,&b),add(b,a); 
	bfs(n);
	for(int i=1;i<=n;++i)
		scanf("%d",&s[i]),w[s[i]]=i+dis[s[i]]-1;
	build(1,1,n);
	for(int i=1;i<=d;++i){
		scanf("%d%d",&x,&y);
		add(1,s[x],y-x);
		add(1,s[y],x-y);
		
		swap(s[x],s[y]);
		printf("%d\n",p[1].w);
	}
	fclose(stdin);fclose(stdout);
	return 0;
}
posted @ 2023-06-04 21:26  andy_lz  阅读(21)  评论(0)    收藏  举报