[NOIP2023] 词典 题解
[NOIP2023] 词典 题解
知识点
贪心,排序。
题意简述
给 \(n\) 个两两不同的长度都为 \(m\) 的单词,每个单词内部可以随意排序。
问在对这 \(n\) 个单词排序后,有哪些有可能成为字典序最小的单词。
分析
首先,如果要让某个单词成为字典序最小的,那么我们只要让它内部升序排序,就达到了该单词能够达到的最小字典序。
但是如果其它单词还有比他小的怎么办呢?我们尝试让它们也变的比该单词大即可,让它们全部降序排序,就达到了它们能够达到的最大字典序。
可是我们是要对全部的单词都做一遍,全部比较一遍就变成了 \(O(n^2m)\),肯定是吃不消的,考虑优化。
发现一个显然的事:我们在考虑某一个单词的时候,只需要和其它中的降序排序后最小的进行比较即可,而除了本身就是最小的那个,对于其它的最小就都固定为它了, 那么预处理出来就可以解决这个问题。
代码
时间复杂度:\(O(nm\log_2{m})\),空间复杂度:\(O(nm)\)。
#define Plus_Cat "dict"
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define FOR(i,a,b) for(int i(a); i<=(int)(b); ++i)
#define DOR(i,a,b) for(int i(a); i>=(int)(b); --i)
#define EDGE(g,i,x,y) for(int i(g.h[x]),y(g[i].v); ~i; y=g[i=g[i].nxt].v)
using namespace std;
constexpr int N(3e3+10);
char tmp[N];
char s[N][N];
int n,m,mi;
bool Greater(char *A,char *B) {
FOR(i,1,m)if(A[i]^B[i])return A[i]<B[i];
return 0;
}
namespace Subtask1 {
bool Check() {
return n<=300;
}
int Cmain() {
if(n==1)return puts("1"),0;
FOR(i,1,n) {
FOR(j,1,m)tmp[j]=s[i][j];
sort(tmp+1,tmp+m+1,greater<>());
FOR(j,1,m)s[i][j]=tmp[j];
}
FOR(i,1,n) {
FOR(j,1,m)tmp[j]=s[i][m-j+1];
bool ans(1);
FOR(j,1,n)if(i!=j&&!Greater(tmp,s[j])) {
ans=0;
break;
}
putchar(ans|'0');
}
puts("");
return 0;
}
}
namespace Subtask {
int Cmain() {
if(n==1)return puts("1"),0;
FOR(i,1,n) {
FOR(j,1,m)tmp[j]=s[i][j];
sort(tmp+1,tmp+m+1,greater<>());
FOR(j,1,m)s[i][j]=tmp[j];
}
mi=1;
FOR(i,2,n)if(Greater(s[i],s[mi]))mi=i;
FOR(i,1,n) {
if(i==mi) {
putchar('1');
continue;
}
FOR(j,1,m)tmp[j]=s[i][m-j+1];
sort(tmp+1,tmp+m+1),putchar(Greater(tmp,s[mi])|'0');
}
puts("");
return 0;
}
}
int main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
scanf("%d%d",&n,&m);
FOR(i,1,n)scanf("%s",s[i]+1);
if(Subtask1::Check())return Subtask1::Cmain();
return Subtask::Cmain();
}
总结
优点
- 这题打了一个部分分进行对拍,是正好适合第一题的策略。
缺点
-
一开始读题读错了。
-
打得太慢了。
-
没有用
std::string,这题非常适合用 STL,但是由于输入输出较麻烦就没用。-
输入:
直接
getchar()快读。 -
输出:
用
putchar()分解了、转c_str()或data()。
#define isblank(ch) ((ch)==' '||(ch)=='\0'||(ch)=='\n'||(ch)=='\r'||(ch)==(EOF)) //... char getc() { return getchar(); } void operator ()(string &x) { static char ch(0); x.clear(); while(ch=getc(),isblank(ch)); do x.push_back(ch); while(ch=getc(),!isblank(ch)); } //... void putc(char c) { putchar(c); } void operator ()(const string s,const char lst='\n') { printf("%s",s.data()),putc(lst); } void operator ()(const string s,const char lst='\n') { printf("%s",s.c_str()),putc(lst); } void operator ()(const string s,const char lst='\n') { for(const char &c:s)putc(c); putc(lst); } -

浙公网安备 33010602011771号