CF1693 ABCD 题解

题目链接:https://codeforces.com/contest/1693

这场的题都非常好啊……
因为现在是从 div1 开始做了,所以可能刚开始会有点吃力(这场我就会做一个 1B 呜呜呜)
1A
先把后缀的极长 0 段删去
考虑对于每一个 右移 操作,首先必然和一个 左移 操作一一对应(最后回到原点了),而且必然存在一种映射,使得每个左移操作的位置在右移操作的右边
也就是说每个 -1 必然在对应的 +1 右边,也就是说前面的前缀和必然都是正数
因此可以对序列做前缀和,如果 \(sum[i] \leq 0,i<n\) 那么必然不符合条件,如果 \(sum[n]\neq 0\) 同样不符合条件

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f,maxn=2e5+5;

int n;
ll a[maxn],sum[maxn];
int solve(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	int up=-1;
	for(int i=n;i>=1;i--)if(a[i]){up=i;break;}
	if(up==-1)return 1;
	for(int i=1;i<=up;i++)sum[i]=sum[i-1]+a[i];
	for(int i=1;i<up;i++)if(sum[i]<=0)return 0;
	if(sum[up])return 0;
	return 1;
}

signed main(){
	int te;scanf("%d",&te);
	while(te--)puts(solve() ? "Yes" : "No");

	return 0;
}

1B
贪心一下,子结点的权值之和一定 >= 父节点的

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;

int n;
vector<int>g[maxn];
int l[maxn], r[maxn], cnt=0;
ll sum[maxn];

void dfs(int x,int fat=0){
	if(g[x].size() == 0){
		sum[fat] += r[x];
		++ cnt;
		return ;
	}
	
	for(int u : g[x]){
		dfs(u,x);
	}
	if(sum[x] > r[x]){
		sum[x] = r[x];
	}else if(sum[x] < l[x]){
		sum[x] = r[x];
		++ cnt;
	}
	sum[fat] += sum[x];
}

void solve(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)g[i].clear(), sum[i] = 0;
	cnt = 0;
	
	for(int i=2;i<=n;i++){
		int x;scanf("%d",&x);
		g[x].pb(i);
	}
	for(int i=1;i<=n;i++)scanf("%d%d",&l[i],&r[i]);
	dfs(1);
	printf("%d\n",cnt);
}

signed main(){
	int te;scanf("%d",&te);
	while(te--)solve();

	return 0;
}

1C
这题好强……
随机走 == 最坏情况下的距离。
\(dis[i]\) 表示 \(i\rightarrow n\) 的答案,那么 \(dis[n] = 0\)
先考虑DAG的情况,考虑点 \(x\),将所有 \(x\) 的后继 \(u\) 按照 \(dis[u]\) 升序排好,那么:
\(dis[x]=min{dis[u]+out[x]-id_u}\) 也就是删掉比 \(dis[u]\) 距离还长的点的边,然后走 \(dis[u]\) (因为此时 \(dis[u]\) 成距离最大的了,即最坏情况)
递推即可(每次可以将图边的方向取反,然后正向递推)
那么考虑有环该如何做?
注意上面的递推过程很像 dijkstra 的实现过程,也就是说每次从堆中取出距离最小的点,因为边权为正,此时这个点的 dis 一定就是到 \(n\) 的最短距离(换言之不会被环更新了),我们这里也可以这么做。
实现和 dijkstra 基本相同,只不过边权变成了与第几次更新到这个点(当然得用 \(degree\) 减之)

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;

vector<int>g[maxn];
int dis[maxn], de[maxn], vis[maxn];
priority_queue<pii>pq; 

signed main(){
	memset(dis,0x3f,sizeof dis);
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y;scanf("%d%d",&x,&y);
		g[y].pb(x);
		++ de[x];
	}
	
	dis[n] = 0;
	pq.push(mpr(-dis[n], n));
	while(!pq.empty()){
		int x = pq.top().second;pq.pop();
		if(vis[x])continue;vis[x] = 1;
		
		for(int u : g[x]){
			if(dis[x]+de[u] < dis[u]){
				dis[u] = dis[x] + de[u];
				pq.push(mpr(-dis[u], u));
			}
			-- de[u];
		}
	}
	printf("%d\n",dis[1]);

	return 0;
}

1D
这题好强……
考虑对于 \([l,r]\) 如何判断是否是 decinc?设 \(dp[i][0]\) 代表第 \(i\) 个位置在上升序列中,下降序列的最后一个元素的最大值(inf 代表下降序列为空,-inf 代表状态不合法)
\(dp[i][1]\) 同理,\(i\) 在下降序列,上升序列的最后一个元素的最小值
转移:
\(dp[i][0] \leftarrow dp[i-1][0], a[i]>a[i-1] \and dp[i-1][0] \neq -inf\)(两个元素都在上升序列)
\(dp[i][0] \leftarrow a[i-1], a[i]>dp[i-1][1]\) (上一个元素在下降序列,这个元素在上升序列)
\(dp[i][1] \leftarrow dp[i-1][1], a[i]<a[i-1] \and dp[i-1][1] \neq inf\)(两个元素都在下降序列)
\(dp[i][1] \leftarrow a[i-1], a[i]<dp[i-1][0]\) (上个上升,这个下降)
最后看一下 \(dp[r][0/1]\) 即可
这样做是 \(O(n^3)\) 的,显然可以通过只枚举左端点变成 \(O(n^2)\)
发现一个性质:一个 dp 值最多会被修改 4 次,因此从后往前枚举复杂度就可以做到 \(O(8n)\)
证明:

代码:

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;

int n,a[maxn];
int dp[maxn][2];
int lst;
ll ans = 0;

void solve(int l){
	dp[l][0] = inf, dp[l][1] = -inf;
	for(int i=l+1;i<=n;i++){
		int w0 = -inf, w1 = inf;
		if(dp[i-1][0] != -inf){
			if(a[i]>a[i-1])w0 = max(w0, dp[i-1][0]);
			if(a[i]<dp[i-1][0])w1 = min(w1, a[i-1]);
		}
		if(dp[i-1][1] != inf){
			if(a[i]<a[i-1])w1 = min(w1, dp[i-1][1]);
			if(a[i]>dp[i-1][1])w0 = max(w0, a[i-1]);
		}
		
//		printf("%d %d %d %d\n",dp[i][0],w0,dp[i][1],w1);
		if(dp[i][0]==w0&&dp[i][1]==w1)break;
		dp[i][0]=w0, dp[i][1]=w1;
		if(dp[i][0]==-inf && dp[i][1] == inf){lst = i-1;break;}
	}
//	printf("! %d %d\n",l,lst);
	ans += lst - l +1;
}

signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	
	lst = n;
	for(int i=n;i>=1;i--)solve(i);
	cout << ans << '\n';

	return 0;
}
posted @ 2023-02-08 11:30  SkyRainWind  阅读(20)  评论(0编辑  收藏  举报