目录
题目链接
T3 P9870 [NOIP2023] 双序列拓展 - 洛谷
T4 P9871 [NOIP2023] 天天爱打卡 - 洛谷
题目报告
比赛时T1 90分 T2 40分 其余0分
赛后T1 T2 T3 AC
赛中状况
详情请见~ NOIP - 2023 游记 ~的考试大况
解题报告
T1.词典 dict
题目大意
给出个长度为
的字符串,每个字符串可以任意调动各字符的位置,问每一个字符串是否能成为字典序最小的一个字符串
题目解析
很明显,本题是一个贪心,只需要求出所有字符串能组成的最大字符串与最小字符串,每判断一个字符串时,拿当前字符串的最小值与其余字符串的最大值比较即可。
在这里也有一个优化,可以找到所有字符串的最大值中的最小值与次小值,去与所有字符串比较即可(优先比较最小值,如果本次判断的字符串与最小值所代表的字符串一致,则使用次小值判断),要注意当的值是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
题目大意
定义 三值逻辑变量:有三种取值,有关系如下:
,
,
.
有个三值逻辑变量
。有
条语句。语句有以下三种类型,其中
表示赋值:
其中
为
的一种
一开始,会给这些变量赋初值,然后按顺序运行这条语句。
希望执行了所有语句后,所有变量的最终值与初值都相等。在此前提下,希望初值中的变量尽可能少。输出
的个数。
题目解析
首先,可以记录下所有的赋值,把无效的全部覆盖掉。之后用赋值关系建图。
建完图之后,会出现三种图:
1.中间有变量赋值的图 - 只需要判断中间赋值的是否是即可,是
加上点的数量
2.中间没有变量赋值的图 - 可以从任意一点出发去染色,看看最后是否有冲突,是加上点的数量
3.一个点 - 判断是否赋值成,是
加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
题目大意
给定序列,称
的扩展序列
,将
写
次构成
。问是否存在长度为
的两个序列
分别为
的扩展且
。
题目解析
可以将两个给定序列组成一个矩阵,每个位置满足条件即可作为可走的位置,去寻找一条从左上角走到右下角的一条路径。
找到序列中所有最小值的位置与
序列中所以最大值的位置,这些位置上的路径都能走,如图。
可以发现这些路径四通八达,我们只需要判断从左上角到最左上角的交点是否能到达,从右下角到最右下角的交点是否能到达即可。
AC代码
暂无