Educational Codeforces Round 130

D. Guess The String

比赛的时候一直在想分治,然后就寄了。首先 \(26\) 次询问 \(1\) 直接提示询问每个不同的字符,问题是那 \(6000\) 次询问 \(2\) —— 由于 \(\log 26<5\),所以这个复杂度实际上是 \(n\cdot \left(\log |\sum|+1\right)\) 的!

首先询问字符串的所有前缀,当不同字符数变化时,询问这个点的字母,反之,二分出在这个位置之前第一个和这个位置的字符相同的字符。为了使二分次数降低至 \(5\),可以只维护 \(26\) 个字符离此位置最近的字符位置,注意二分后要进行更新。

E. Coloring

根据题设条件可以发现,对于染同一颜色的点集,它们一定两两距离相等且为最小距离,其中一个点的最小距离定义为离这个点最近的点与其的距离。所以先 \(\rm dfs\) 出所有可以染同一颜色的点集,显然它们两两不交,那么对于一个点集内的点,要么染一种颜色,要么所有点颜色都不同。于是直接 \(\mathtt{dp}\) 即可。

F. Too Many Constraints

已知:这是一道 \(\text{2-sat}\) 题。我真的不太能想到这题的 motivation.

先考虑 \(a_i\ne x\) 的限制,显然不能给每个点建 \(k\) 个变量,代表 \(a_i\) 是否取 \(x\),因为这是一个 \(k\text{-sat}\) 问题 ouo。我们把一个点拆得多些 —— 对于 \(a_i\),有 \(V_{i,j,0/1}\)\(0\) 表示 \(a_i>j\) 成立,\(1\) 表示 \(a_i\leqslant j\) 即其逆命题成立。

于是 \(a_i\ne x\) 就相当于 \(a_i>x\)\(a_i\leqslant x-1\) 成立。其它情况也是容易推导的。复杂度 \(\mathcal O(k\cdot (n+m))\).

特别需要注意边界情况:

  • 由于有 \(a_i>x\),所以 \(x\) 的取值范围需要扩张到 \([0,V]\)
  • 可以发现,\(a_i\leqslant 0\)\(a_i>V\) 都是不合法的,同时我们发现,当 \(a_i\leqslant 0\) 满足时,\(a_1\leqslant 0\) 一定满足,所以实际上只需要将 \(V_{1,0,1}\) 向它的逆连边即可,\(a_i>V\) 同理。

最后再特别提一下选取变量的问题:

\(\text{bel}(i)\)\(i\) 所在强连通分量的拓扑序编号(由于我写的是 \(\rm tarjan\),所以编号小的拓扑序更大),如果 \(\text{bel}(i)<\text{bel}(i')\) 则选取 \(i\),否则选取 \(i\) 的逆即 \(i'\).

要证明上述构造是正确的,我们只需要证明所有被选取点不能到达没有被选取的点。使用反证法,对于被选取点 \(x\) 和未被选取点 \(y\),假定存在 \(x\rightarrow y\) 的路径,那么有 \(\text{bel}(x)\geqslant \text{bel}(y)\)。同时根据是否被选取我们可以知道 \(\text{bel}(x)<\text{bel}(x'),\text{bel}(y)>\text{bel}(y')\),所以可以推出 \(\text{bel}(x')>\text{bel}(x)\geqslant \text{bel}(y)>\text{bel}(y')\).

但是 根据对称性,这里存在一条 \(y'\rightarrow x'\) 的路径,即 \(\text{bel}(y')\geqslant \text{bel}(x')\),矛盾。

最后的最后,如果存在 \(x\rightarrow y\) 的限制,且 \(y\) 是不合条件的 必须进行 连边!

AtCoder Beginner Contest 209

E - Shiritori

博主的 \(\rm bibi\) 时间

当大家都在随切 \(\rm arc\) 的时候,我 \(\rm abc\) 都做不来 😭

Solution

设每个串前三位为 \(u\),后三位为 \(v\). 先开始想的是从 \(u\)\(v\) 连边,但是判 Draw 即判环会很难。因为环上某点可能有非 Draw 解。所以反着建边跑拓扑排序,就可以将所有值初始为 Draw,规避掉这个问题。

Code

#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;
}

F - Deforestation

Solution

尝试将问题简单化,先考虑树 \(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\) 棵树,所以 \(k\le 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}\) 是因为在取 \(i\) 之前取了 \(j-1\) 棵树,\(i+1\) 正好可以插入到第 \(j\) 位,排在 \(i\) 之前。

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

一个有趣的现象:自己在实现时将 '<' 打反了,但还是通过了此题。其实 "矮优先" 和 "高优先" 是等价的,可以把砍伐序列倒过来看来理解。

Code

#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],'\n');
	return 0;
}
posted on 2021-07-13 11:58  Oxide  阅读(62)  评论(0编辑  收藏  举报