BestCoder Round #74 (div.2) 题面&题解

       以下吐槽时间请无视:woc你TM再给我两分钟!!再给我两分钟!!我就可以A了T4了。。。赛后交oj真的是1A啊。。。1A啊!!!!!那score就上4500!!!就rank2了!!!那就上div1了呀(雾)。。。。woc上帝再给我两分钟吧T_T~~T_T~~

       T1:LCP Array

       题意:已知一个字符串相邻两个后缀(指下标相邻)的最长共同前缀,求可行的字符串个数。

       我们发现,如果a[i]>0,那么显然有s[i]=s[i+1]。并且可以推理得必有a[i]=a[i+1]+1或者a[i]=0,这样就可以判断无解输出0了。另一方面,我们发现s[1]有26种取法,另外如果s[i]!=s[i+1]那么s[i+1]有25种取法否则有1种取法。那么用乘法原理累乘起来就好了。

       时间复杂度O(N)。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 1000000007
using namespace std;

int n,a[1000005];
int solve(){
	int i; ll ans=26;
	for (i=1; i<n; i++) scanf("%d",&a[i]); a[n]=0;
	for (i=n-1; i; i--){
		if (!a[i]) ans=ans*25%1000000007;
			else if (a[i]!=a[i+1]+1) return 0;
	}
	return ans;
}
int main(){
	int cas; scanf("%d",&cas);
	while (cas--){
		scanf("%d",&n);
		printf("%d\n",solve());
	}
	return 0;
}

       T2:Shortest Path

       题意:一开始所有点都在一条链上,满足dist(i,i+1)=1,现在加入三条边(i,j)且dist(i,j)=1,求s-t的最短路。多组询问。

       显然s-t一定是从s出发,走过若干个新加的边,最后到达t。那么可以用dfs枚举新加入的三条边(i,j)加入的顺序,加入时需要枚举方向,然后再求最短路就行了(语死早具体看代码)。

       时间复杂度O(MN!2^N),其中N=3。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#define mod 1000000007
#define ll long long
using namespace std;

int n,m,ans,x,y,a[10][2]; bool bo[10];
void dfs(int k,int now,int d){
	if (d+abs(now-y)<ans) ans=d+abs(now-y);
	if (k>3) return; int i;
	for (i=1; i<=3; i++) if (bo[i]){
		bo[i]=0;
		dfs(k+1,a[i][1],d+abs(now-a[i][0])+1);
		dfs(k+1,a[i][0],d+abs(now-a[i][1])+1);
		bo[i]=1;
	}
}
int main(){
	int cas; scanf("%d",&cas);
	while (cas--){
		scanf("%d%d",&n,&m); int i;
		for (i=1; i<=3; i++) scanf("%d%d",&a[i][0],&a[i][1]);
		memset(bo,1,sizeof(bo)); ll sum=0;
		for (i=1; i<=m; i++){
			scanf("%d%d",&x,&y);
			ans=abs(y-x); dfs(1,x,0);
			sum=(sum+(ll)ans*i)%mod;
		}
		printf("%I64d\n",sum);
	}
	return 0;
}

       T3:Transform

       题意:有两种变换:1.将x异或某个2的幂;2.将x异或某个给定的ai;多次询问从S变换到T的最小步数。

       相当于求0-S^T的最短步数。预处理后O(1)判断。

       时间复杂度O(A(logA+N)+M)。其中A表示>=ai的最小的2的幂,本题为2^17。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 1000000007
using namespace std;

int n,m,a[25],h[300005],d[300005],bin[25];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
int main(){
	int cas=read(),i;
	bin[0]=1; for (i=1; i<=17; i++) bin[i]=bin[i-1]<<1;
	while (cas--){
		n=read(); m=read();
		memset(d,-1,sizeof(d)); d[0]=0;
		for (i=1; i<=n; i++) scanf("%d",&a[i]);
		int head=0,tail=1; h[1]=0;
		while (head<tail){
			int x=h[++head];
			for (i=1; i<=n; i++){
				int y=x^a[i];
				if (d[y]==-1){ d[y]=d[x]+1; h[++tail]=y; }
			}
			for (i=0; i<17; i++){
				int y=x^bin[i];
				if (d[y]==-1){ d[y]=d[x]+1; h[++tail]=y; }
			}
		}
		ll ans=0;
		for (i=1; i<=m; i++){
			int x=read(),y=read(); ans=(ans+(ll)i*d[x^y])%mod;
		}
		printf("%I64d\n",ans);
	}
	return 0;
}

       T4:Toposort

       题意:给定一个N点M边的DAG,可以删去恰好k条边,求字典序最小的可行拓扑序列。

       显然,如果现在x是所有删去<=k条边可以使得入度=0的最小的点(即入度<=k的点),那么一定要先把x给弄出来(就是把连向x的边删掉)。那么我们用一个堆,维护所有入度<=k的点,然后每次取出最小的点作为拓扑序列的下一个值,然后把这条边的出边删掉,同时k减去这个点的入度。注意k是会减小的,因此每次需要把堆中入度>k的边删去,具体不需要每次更新,只需要取最小值的时候判断一下即可。

       会不会一次删很多个而影响时间呢?不会。考虑边为M,因而将点加入的次数一定<=M(注意一个点虽然有可能被反复加入又删掉很多次,但是这个点作为一个特定的边到达的点只会被加入1次),那么删除也最多删除M次,因此不会影响时间。

       时间复杂度O((N+M)logN)。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
#define mod 1000000007
#define N 500005
using namespace std;

int n,m,tot,k,fst[N],pnt[N],nxt[N],etr[N]; bool bo[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
priority_queue<int,vector<int>,greater<int> > q;
int main(){
	int cas=read();
	while (cas--){
		n=read(); m=read(); k=read(); int i;
		tot=0; for (i=1; i<=n; i++){ bo[i]=1; fst[i]=etr[i]=0; }
		for (i=1; i<=m; i++){
			int u=read(),v=read();
			add(u,v); etr[v]++;
		}
		for (i=1; i<=n; i++) if (etr[i]<=k){ bo[i]=0; q.push(i); }
		int cnt=0; ll sum=0;
		while (!q.empty()){
			while (etr[q.top()]>k){ bo[q.top()]=1; q.pop(); }
			int x=q.top(),p; q.pop(); k-=etr[x];
			cnt++; sum=(sum+(ll)cnt*x)%mod; etr[x]=0;
			for (p=fst[x]; p; p=nxt[p]){
				int y=pnt[p];
				if (etr[y]){ etr[y]--; if (etr[y]<=k && bo[y]){ bo[y]=0; q.push(y); } }
			}
		}
		printf("%I64d\n",sum);
	}
	return 0;
}

       第一场bestcoder,打了rank4,看起来还好但是。。实际上不太好啊。。div1的题目还没有看过,就不写了。

by lych

2016.3.5


     


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值