AtCoder Beginner Contest 291 DEF

nannandbk / 2023-06-17 / 原文

AtCoder Beginner Contest 291

D - Flip Cards

Problem Statement

题意:\(N\)张卡片,编号\(1\)\(N\),每张卡片有正反两面,写有数字,初始状态都是正面朝上。

考虑每张卡片的翻转情况,选择翻或者不翻,求是的相邻两张卡片的数字不同,求方案数,答案模\(998244353\)

Solution

题解:求方案数,想到\(DP\)。对于每张卡片,无非两种情况,翻或者不翻,且该张卡片的翻转情况只和上一张卡片有关,那我们定义\(dp[idx][0/1]\)为上一张卡片编号为\(idx\),翻转情况用\(flag = 0/1\)表示,\(0\)表示不翻\(1\)表示翻。注意要记忆化搜索一下,不然会\(TLE\).

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

typedef long long ll;
const int N = 2e5+10;
ll dp[N][3];

struct modular_arithmetic {
    const int mod = 998244353;
    int add(ll a, ll b) {
        return (a % mod + b % mod) % mod;
    }
    int mul(ll a, ll b) {
        return ((a % mod) * (b % mod)) % mod;
    }
    int pow(ll x, ll n) {
        if (n == 0) return 1;
        int res = pow(x, n / 2);
        res = mul(res, res);
        if (n % 2) res = mul(x, res);
        return res;
    }
    int inv(ll x) {
        return pow(x, mod - 2);
    }
    int div(ll a, ll b) {
        return mul(a, inv(b));
    }
};
modular_arithmetic mod;
int n;
int a[N][3];
ll dfs(int idx,int flag)
{
	if(idx>n)
		return 1;
	ll &ret = dp[idx][flag];
	if(~dp[idx][flag])return ret;
	ret = 0;
	if(a[idx][0]!=a[idx-1][flag])
		ret = mod.add(ret,dfs(idx+1,0));
	if(a[idx][1]!=a[idx-1][flag])
		ret = mod.add(ret,dfs(idx+1,1));
	return ret;
}


int main()
{
	
	cin>>n;
	for(int i = 1;i<=n;i++)
	{
		cin>>a[i][0]>>a[i][1];
	}
	memset(dp,-1,sizeof(dp));
	cout<<mod.add(dfs(2,0),dfs(2,1))<<endl;
	return 0;
}

E - Find Permutation

Problem Statement

题意:\(A\)是一个排列,告诉你\(M\)组排列中不同位置数的大小关系,问能否找出唯一一个符合题意的\(A\)

Solution

题解:\(Topo\)排序,根据题目给的位置大小关系连边建图。

对于\(Topo\)排序:

规则

  • 图中每个顶点只出现一次

  • A在B前面,则不存在B在A前面的路径。(不能成环!!!!)

  • 顶点的顺序是保证所有指向它的下个节点在被指节点前面!(例如A—>B—>C那么A一定在B前面,B一定在C前面)。所以,这个核心规则下只要满足即可,所以拓扑排序序列不一定唯一

    题目要找唯一的,我们注意判一下。

    不行的情况

    1. 入度为0的点有多个或无

    2. 有环。但因为我们用了\(Topo\)就不用再特意判了。

      #include<bits/stdc++.h>
      using namespace std;
      const int N = 2e5+10;
      vector<int>edge[N];
      int n,m,d[N],a[N];
      map<pair<int,int>,int>mp;
      queue<int>q;
      int now;
      inline void TopoSort()
      {
      	now = n;
      	for(int i = 1;i<=n;i++)
      	{
      		if(!d[i])
      		{
      			q.push(i);
      		}
      	}
      	if(q.size()>1)//入度为0点不止1个
      	{
      		cout<<"No\n";
      		return;
      	}
      	while(!q.empty())
      	{
      		int x = q.front();
      		q.pop();
      		a[x] = now--;
      		for(auto y:edge[x])
      		{
      			if(--d[y]==0)
      			{
      				q.push(y);
      			}
      		}
      		if(q.size()>1)
      		{
      			cout<<"No\n";
      			return;
      		}
      	}
      
      		for(int i = 1; i <= n; i++) 
      		{
              	if(a[i] == 0) //有点没连上
              	{
                  	cout << "No" << endl;
                  	return;
              	}
              }
       
      		 cout<<"Yes\n";
      		 for(int i = 1;i<=n;i++)
      		 	cout<<a[i]<<" ";
      		 cout<<endl;
      }
      
      
      int main()
      {
      	ios::sync_with_stdio(false);
      	cin.tie(0);
      	cout.tie(0);
      	cin>>n>>m;
      	for(int i =1 ;i<=m;i++)
      	{
      		int x,y;
      		cin>>x>>y;
      		if(mp[{y,x}])continue;//注意可能有重边,continue掉
      		mp[{y,x}]++;
      		edge[y].push_back(x);
      		++d[x];
      	}
      	TopoSort();
      	return 0;
      }
      

F - Teleporter and Closed off

Problem Statement

题意:给你\(N\)个城市,编号从\(1\)\(N\)。给你\(N\)个字符串代表是否能从一个城市传送到另一个城市。

问:你可以从\(1\)\(N\)不经过第\(k\)个城市吗?如果可以输出最小的使用传送机次数,否则输出\(-1\)

Solution

题解:显然是最短路问题,题目中说求从\(1\)\(N\)要不能经过第\(k\)号城市的最短路,我们考虑用bfs求\(1\)到所有点的最短路d1,再建反向边求\(n\)到所有点的距离d2。

对于\(x\)\(y\),如果\(d1x\)\(d2y\)都存在,且\(d1x+d2y+1\)的距离就是\(1\)\(N\)的最短路,且该路径必然不经过\([x+1,y-1]\)的。

#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int>bfs(vector<vector<int>>&edge,int s)
{
	vector<int>dist(n+10,-1);
	dist[s] = 0;
	queue<int>q;
	q.push(s);
	while(!q.empty())
	{
		int x = q.front();
		q.pop();
		for(auto y:edge[x])
		{
			if(dist[y]==-1)
			{
				dist[y] = dist[x]+1;
				q.push(y);
			}
		}
	}
	return dist;
}

int main()
{
	cin>>n>>m;
	vector<vector<int>>edge(n+10),edge2(n+10);
	for(int i = 1;i<=n;i++)
	{
		string s;
		cin>>s;
		s = "?"+s;
		for(int j = 1;j<=m;j++)
		{
			if(s[j]=='1')
			{
				edge[i].push_back(i+j);
				edge2[i+j].push_back(i);
			}
		}
	}
	vector<int> d1,d2,ans(n,-1);
	d1=bfs(edge,1);
	d2=bfs(edge2,n);	

	for(int i = 1;i<=n;i++)
	{
		for(auto &x:edge[i])
		{
			if(d1[i]==-1||d2[x]==-1)continue;
			int t = d1[i]+d2[x]+1;
			for(int j = i+1;j<x;j++)
			{
				if(ans[j]==-1||t<ans[j])
					ans[j] = t;
			}
		}
	}
	for(int i = 2;i<n;i++)
		cout<<ans[i]<<" ";
	cout<<endl;
	return 0;
}