AtCoder Beginner Contest 209

(sf E-Shiritori)

博主的 ( m bibi) 时间

“怎么会有人连 ( m abc) 都不会做啊?”

解法

设每个串前三位为 (u),后三位为 (v)

先开始想的是从 (u)(v) 连边,但是判 Draw 即判环会很难。因为环上某点可能有非 Draw 解。

反着建边就行了。

代码

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

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

const int maxn=4e5+5;

int n,len,idx,in[maxn],ans[maxn];
char s[maxn][10];
map <string,int> mp;
vector <int> e[maxn];
queue <int> q;

void Go() {
	rep(i,1,idx) {
		ans[i]=-1;
		if(!in[i]) q.push(i),ans[i]=0;
	}
	while(!q.empty()) {
		int t=q.front(); q.pop();
		for(int i=0;i<e[t].size();++i) {
			int v=e[t][i];
			if(ans[v]==-1) {
				--in[v];
				if(!ans[t]) ans[v]=1,q.push(v);
				else if(!in[v]) ans[v]=0,q.push(v);
			}
		}
	}
}

int main() {
	string u,v; int x,y;
	n=read(9);
	rep(i,1,n) {
		scanf("%s",s[i]+1); len=strlen(s[i]+1);
		u=""; u+=s[i][1];
		u+=s[i][2],u+=s[i][3];
		v=""; v+=s[i][len-2];
		v+=s[i][len-1],v+=s[i][len];
		if(!mp[u]) mp[u]=++idx;
		if(!mp[v]) mp[v]=++idx;
		x=mp[u],y=mp[v];
		e[y].push_back(x); ++in[x];
	}
	Go();
	rep(i,1,n) {
		len=strlen(s[i]+1);
		v=""; v+=s[i][len-2];
		v+=s[i][len-1],v+=s[i][len];
		y=mp[v];
		if(ans[y]==-1) puts("Draw");
		else if(ans[y]) puts("Aoki");
		else puts("Takahashi");
	}
	return 0;
}

(sf F-Deforestation)

解法

尝试将问题简单化,先考虑树 (i)(i+1)

容易发现,这两棵树被砍的顺序并不影响除它们之外树的贡献。而先砍 (i) 贡献为 (h_i+2h_{i+1}),先砍 (i+1) 贡献为 (2h_i+h_{i+1})

为了使花费最小化,我们应该先砍 (h_i,h_{i+1}) 中较高的一棵。

(dp_{i,j}) 为前 (i) 棵树,第 (i) 棵树是第 (j) 个被砍的砍伐排列数。

  1. (h_i=h_{i+1})(dp_{i+1,j}=sum_{k=1}^{i}dp_{i,k})。相当于没有限制,但由于只砍了前 (i) 棵树,所以 (kle i)
  2. (h_i>h_{i+1})(dp_{i+1,j}=sum_{k=1}^{j-1} dp_{i,k})
  3. (h_i<h_{i+1})(dp_{i+1,j}=sum_{k=j}^i dp_{i,k})。取 (dp_{i,j}) 则是因为 (h_{i+1}) 本质上是插入进砍伐序列(位置固定),所以虽然此时 (h_i) 排在第 (j+1) 位,它实际只需统计 (j) 棵树。

用前缀和即可优化到 (mathcal O(n^2))

另:自己在实现时将 < 打反了,但还是过了此题。其实 “矮优先” 和 “高优先” 是等价的,可以把砍伐序列倒过来看来理解。

代码

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

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

const int maxn=4005,mod=1e9+7;

int n,h[maxn],dp[2][maxn];

int main() {
	n=read(9);
	rep(i,1,n) h[i]=read(9);
	dp[1][1]=1;
	rep(i,2,n) rep(j,1,i) {
		if(h[i]==h[i-1]) dp[i&1][j]=dp[i&1^1][i-1];
		else if(h[i]<h[i-1]) dp[i&1][j]=dp[i&1^1][j-1];
		else dp[i&1][j]=(dp[i&1^1][i-1]-dp[i&1^1][j-1]+mod)%mod;
		dp[i&1][j]=(dp[i&1][j]+dp[i&1][j-1])%mod;
	}
	print(dp[n&1][n],'
');
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/15005821.html