NOIP 2023 补题报告

目录

题目链接

题目报告

赛中状况

解题报告

T1.词典 dict

题目大意

题目解析

AC代码

T2.三值逻辑 tribool

题目大意

题目解析

AC代码

T3.双序列拓展 expand

题目大意

题目解析

AC代码 


题目链接

T1 P9868 [NOIP2023] 词典 - 洛谷

T2 P9869 [NOIP2023] 三值逻辑 - 洛谷

T3 P9870 [NOIP2023] 双序列拓展 - 洛谷

T4 P9871 [NOIP2023] 天天爱打卡 - 洛谷

题目报告

比赛时T1 90分 T2 40分 其余0分

赛后T1 T2 T3 AC

赛中状况

详情请见~ NOIP - 2023 游记 ~的考试大况

解题报告

T1.词典 dict

题目大意

给出n个长度为m的字符串,每个字符串可以任意调动各字符的位置,问每一个字符串是否能成为字典序最小的一个字符串

题目解析

很明显,本题是一个贪心,只需要求出所有字符串能组成的最大字符串与最小字符串,每判断一个字符串时,拿当前字符串的最小值与其余字符串的最大值比较即可。

在这里也有一个优化,可以找到所有字符串的最大值中的最小值与次小值,去与所有字符串比较即可(优先比较最小值,如果本次判断的字符串与最小值所代表的字符串一致,则使用次小值判断),要注意当n的值是1时,要特殊判断(我就是这么错的)。

AC代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>//unordered
#include <set>
#include <list>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const ll N = 3005;
const ll M = 10005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1000000007;

ll ppow(ll x, ll y, ll MOD){
	ll ans = 1;
	while(y) {
		if(y & 1) ans = ans * x, ans = ans % MOD;
		y = y >> 1, x = x * x, x = x % MOD;
	}
	return ans;
}

#define file(FILENAME) freopen(FILENAME ".in", "r", stdin), freopen(FILENAME ".out", "w", stdout);
#define bug printf("--------\n");
#define debug(x) cout<< #x << '=' << x << '\n';

ll n,m;
string a_min[N];
string a_max[N];
void zuhe(string s, int x){
	ll word[31]={0};
	for(int i=0;i<m;i++){
		word[(int)(s[i]-'a'+1)]++;
	}
	string s_min;
	for(int i=1;i<=26;i++){
		for(int j=1;j<=word[i];j++){
			s_min+=(char)(i+'a'-1);
		}
	}
	string s_max;
	for(int i=26;i>=1;i--){
		for(int j=1;j<=word[i];j++){
			s_max+=(char)(i+'a'-1);
		}
	}
	a_min[x]=s_min;
	a_max[x]=s_max;
}
int main() {
	// file("dict")
	cin>>n>>m;
	char ss[N];
	for(int i=1;i<=n;i++){
		scanf("%s",ss);
		string s;
		for(int j=0;j<m;j++){
			s+=ss[j];
		}
		zuhe(s,i);
	}
	if(n==1){cout<<1;return 0;}//这就是值10分的那一行 考后加的
	string min1=a_max[1];
	string min2;
	int mini=1;
	for(int i=2;i<=n;i++){
		if(min1>a_max[i]){min1=a_max[i],mini=i;}
	}
	if(mini==1)min2=a_max[2];
	else min2=a_max[1];
	for(int i=1;i<=n;i++){
		if(i==mini)continue;
		if(min2>a_max[i]){min2=a_max[i];}
	}
	for(int i=1;i<=n;i++){
		bool flag=1;
		if(i==mini){
			if(a_min[i]>min2)flag=0;
		}
		else{
			if(a_min[i]>min1)flag=0;
		}
		cout<<flag;
	}
	return 0;
}

T2.三值逻辑 tribool

题目大意

定义 三值逻辑变量:有T,F,U三种取值,有关系如下:

\sim T=F,\sim F=T,\sim U=U.

n个三值逻辑变量x_1,...,x_n。有m条语句。语句有以下三种类型,其中\leftarrow表示赋值:

  1. x_i\leftarrow v 其中vT,F,U的一种
  2. x_i\leftarrow x_j
  3. x_i\leftarrow \sim x_j

一开始,会给这些变量赋初值,然后按顺序运行这m条语句。

希望执行了所有语句后,所有变量的最终值与初值都相等。在此前提下,希望初值中U的变量尽可能少。输出U的个数。

题目解析

首先,可以记录下所有的赋值,把无效的全部覆盖掉。之后用赋值关系建图。

建完图之后,会出现三种图:

1.中间有变量赋值的图 - 只需要判断中间赋值的是否是U即可,是ans加上点的数量

2.中间没有变量赋值的图 - 可以从任意一点出发去染色,看看最后是否有冲突,是ans加上点的数量

3.一个点 - 判断是否赋值成U,是ans加1

AC代码

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
#define TIE ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e5 + 10, M = 1e5 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const ll mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int n, m;
vector<vector<pii>> e(N);
pii a[N];  // first 0:TFU  second:T 0 F 1 U 2
// 1:xj、-xj    second:xj  -xj
bool st[N];  // 标记当前这个点是否走过
int sze = 0;
int color[N];
bool flag;
void dfs(int x) {
    sze++;
    st[x] = true;
    for (int i = 0; i < e[x].size(); i++) {
        int v = e[x][i].first;
        if (!st[v])
            dfs(v);
    }
}
void dfs2(int x) {
    sze++;
    st[x] = true;
    for (int i = 0; i < e[x].size(); i++) {
        int t = e[x][i].first, v = e[x][i].second;
        if (!st[t]) {
            // 当前点的颜色为color[x],邻接点应该是?
            // 正边赋成自己,反边赋成另一个颜色
            color[t] = (color[x] ^ v);
            dfs2(t);
        } else {
            // 如果原先点有颜色
            if (color[t] != (color[x] ^ v))
                flag = false;
        }
    }
}
void solve() {
    // 清空
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        e[i].clear();
    memset(st, 0, sizeof st);
    for (int i = 1; i <= n; i++) {
        a[i] = make_pair(1, i);
    }
    for (int i = 1; i <= m; i++) {
        string s;
        cin >> s;
        int x, y;
        if (s == "+") {
            cin >> x >> y;
            a[x] = a[y];
        } else if (s == "-") {
            cin >> x >> y;
            if (a[y].first == 1) {
                a[x] = make_pair(1, -a[y].second);
            } else if (a[y].second <= 1) {  // T F
                a[x] = make_pair(0, 1 - a[y].second);
            } else {
                a[x] = make_pair(0, 2);
            }
        } else {
            cin >> x;
            if (s == "T")
                a[x] = make_pair(0, 0);
            else if (s == "F")
                a[x] = make_pair(0, 1);
            else if (s == "U")
                a[x] = make_pair(0, 2);
        }
    }
    // 建图!  xj  -xj的点建图
    for (int i = 1; i <= n; i++) {
        if (a[i].first == 1) {
            int v = abs(a[i].second);  // ?
            // 能到达i点
            // 边权  正   负
            // 判断v和a[i].second是不是同一个点  是:正
            int w = (v != a[i].second);
            e[i].push_back(make_pair(v, w));
            e[v].push_back(make_pair(i, w));
        }
    }
    int ans = 0;
    // 有三种连通块
    for (int i = 1; i <= n; i++) {
        if (a[i].first == 0 && !st[i]) {
            sze = 0;
            dfs(i);  // 记录当前连通块的大小
            // 孤点记录出来
            if (a[i].second == 2)
                ans += sze;
        }
    }
    for (int i = 1; i <= n; i++) {
        if (!st[i]) {  // 这就一定是a[i].first == 1
            sze = 0;
            color[i] = 0;  // T
            flag = true;
            dfs2(i);  // 染色
            if (!flag)
                ans += sze;
        }
    }
    cout << ans << "\n";
}
int main() {
    TIE;
    int c, t;
    cin >> c >> t;
    while (t--) {
        solve();
    }
    return 0;
}

T3.双序列拓展 expand

题目大意

给定序列x(len=n),y(len=m),称f的扩展序列g:\exists c,将f_ic_i> 1次构成g。问是否存在长度为10^{100}的两个序列a,b分别为x,y的扩展且\forall (a_i-b_i)(a_j-b_j)>0

题目解析

可以将两个给定序列组成一个矩阵,每个位置满足条件即可作为可走的位置,去寻找一条从左上角走到右下角的一条路径。

找到x序列中所有最小值的位置与y序列中所以最大值的位置,这些位置上的路径都能走,如图。

可以发现这些路径四通八达,我们只需要判断从左上角到最左上角的交点是否能到达,从右下角到最右下角的交点是否能到达即可。

AC代码 

暂无

  • 33
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值