【题目描述】
小 H 与小 Y 刚刚参加完 UOIP 外卡组的初赛,就迫不及待的跑出考场对答案。
“吔,我的答案和你都不一样!”,小 Y 说道,”我们去找神犇们问答案吧”。
外卡组试卷中共有 m 道判断题,小 H 与小 Y 一共从其他 n 个神犇那问了答案。之后又
从小 G 那里得知, 这 n 个神犇中有 p 个考了满分, q 个考了零分, 其他神犇不为满分或零分。
这可让小 Y 与小 H 犯了难。你能帮助他们还原出标准答案吗?如有多解则输出字典序最小
的那个。无解输出-1。
【 输入格式】
第一行四个整数 n, m, p, q,意义如上描述。
接下来 n 行,每一行 m 个字符’N’或’Y’,表示这题这个神犇的答案。
【 输出格式】
仅一行,一个长度为 m 的字符串或是-1。
【 样例输入】
2 2 2 0
YY
YY
【 样例输出】
YY
【 数据范围】
30% : n <= 100.
60% : n <= 5000 , m <= 100.
100% : 1 <= n <= 30000 , 1 <= m <= 500. 0 <= p , q 且 p + q <= n.
長樂一中給出的解析:
30%: O(n ^ 2 * m)暴力判断。
100%: 很显然答案的可能性最多只有 n 种,所以我们将所有人的答案按字典序排序后枚举
将每个人的答案作为正确答案来进行判断。 由于是判断题, 若当前人的答案为正确答
案则零分者的答案也就确定了, 那么只需统计出这两种答案的人数判断是否满足题意
即可。这一步使用字符串哈希即可解决。
另外要注意 p = 0 和 p = q = 0 的情况。
長樂一中給出的正解代碼:
1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 #include <cmath> 6 using namespace std; 7 8 const int N = 3e4 + 2, M = 5e2 + 2, sed = 31, SED = 131, mod = 70177, MOD = 92311; 9 int n, m, p, q, ans, hash[N], HASH[N]; 10 int top, info[mod], nxt[N * 2], fet[N * 2], cnt[N * 2]; 11 struct node { 12 char s[M]; 13 inline bool operator < (const node &b) const { 14 return strcmp(s, b.s) < 0; 15 } 16 } a[N]; 17 18 inline void Insert(const int &x, const int &y) { 19 for (int k = info[x]; k; k = nxt[k]) 20 if (fet[k] == y) { 21 ++cnt[k]; return ; 22 } 23 nxt[++top] = info[x]; info[x] = top; 24 fet[top] = y; cnt[top] = 1; 25 return ; 26 } 27 28 inline int Query(const int &x, const int &y) { 29 for (int k = info[x]; k; k = nxt[k]) 30 if (fet[k] == y) return cnt[k]; 31 return 0; 32 } 33 34 inline void Solve1() { 35 int tmp, TMP; ans = -1; 36 for (int i = 0; i < n; ++i) { 37 tmp = TMP = 0; 38 for (int j = 0; j < m; ++j) { 39 tmp = (tmp * sed + (a[i].s[j] == 'N')) % mod; 40 TMP = (TMP * SED + (a[i].s[j] == 'N')) % MOD; 41 } 42 hash[i] = tmp, HASH[i] = TMP; 43 Insert(tmp, TMP); 44 } 45 for (int i = 0; i < n; ++i) 46 if (Query(hash[i], HASH[i]) == p) { 47 tmp = TMP = 0; 48 for (int j = 0; j < m; ++j) { 49 tmp = (tmp * sed + (a[i].s[j] == 'Y')) % mod; 50 TMP = (TMP * SED + (a[i].s[j] == 'Y')) % MOD; 51 } 52 if (Query(tmp, TMP) == q) { 53 ans = i; break; 54 } 55 } 56 if (ans != -1) printf("%s ", a[ans].s); 57 else puts("-1"); 58 return ; 59 } 60 61 char cur[M]; 62 inline void Solve2() { 63 int tmp, TMP; ans = -1; 64 for (int i = 0; i < n; ++i) { 65 tmp = TMP = 0; 66 for (int j = 0; j < m; ++j) { 67 tmp = (tmp * sed + (a[i].s[j] == 'N')) % mod; 68 TMP = (TMP * SED + (a[i].s[j] == 'N')) % MOD; 69 } 70 hash[i] = tmp, HASH[i] = TMP; 71 Insert(tmp, TMP); 72 } 73 for (int i = n - 1; i >= 0; --i) 74 if (Query(hash[i], HASH[i]) == q) { 75 tmp = TMP = 0; 76 for (int j = 0; j < m; ++j) { 77 tmp = (tmp * sed + (a[i].s[j] == 'Y')) % mod; 78 TMP = (TMP * SED + (a[i].s[j] == 'Y')) % MOD; 79 } 80 if (Query(tmp, TMP) == p) { 81 ans = i; break; 82 } 83 } 84 if (ans != -1) { 85 for (int i = 0; i < m; ++i) 86 cur[i] = a[ans].s[i] == 'N' ? 'Y' : 'N'; 87 printf("%s ", cur); 88 } 89 else puts("-1"); 90 return ; 91 } 92 93 void Solve3() { 94 int tmp, TMP; 95 for (int i = 0; i < n; ++i) { 96 tmp = TMP = 0; 97 for (int j = 0; j < m; ++j) { 98 tmp = (tmp * sed + (a[i].s[j] == 'N')) % mod; 99 TMP = (TMP * SED + (a[i].s[j] == 'N')) % MOD; 100 } 101 Insert(tmp, TMP); 102 tmp = TMP = 0; 103 for (int j = 0; j < m; ++j) { 104 tmp = (tmp * sed + (a[i].s[j] == 'Y')) % mod; 105 TMP = (TMP * SED + (a[i].s[j] == 'Y')) % MOD; 106 } 107 Insert(tmp, TMP); 108 } 109 bool flag = true; 110 for (int i = 0; i < m; ++i) cur[i] = 'N'; 111 do { 112 tmp = TMP = 0; 113 for (int j = 0; j < m; ++j) { 114 tmp = (tmp * sed + (cur[j] == 'N')) % mod; 115 TMP = (TMP * SED + (cur[j] == 'N')) % MOD; 116 } 117 if (Query(tmp, TMP) == 0) { 118 flag = true; break; 119 } 120 flag = false; 121 for (int j = m - 1; j >= 0; --j) 122 if (cur[j] == 'Y') cur[j] = 'N'; 123 else { 124 cur[j] = 'Y'; flag = true; break; 125 } 126 } while (flag); 127 if (flag) printf("%s ", cur); 128 else puts("-1"); 129 return ; 130 } 131 132 int main() { 133 freopen("answer.in", "r", stdin); 134 freopen("answer.out", "w", stdout); 135 scanf("%d%d%d%d", &n, &m, &p, &q); 136 for (int i = 0; i < n; ++i) scanf("%s", a[i].s); 137 sort(a, a + n); 138 if (p) Solve1(); 139 else if (q) Solve2(); 140 else Solve3(); 141 fclose(stdin); fclose(stdout); 142 return 0; 143 }
代碼實現:
#include<map> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,m,p,q,a,z=1; char ch[500],cn[500]; string st[30000];//儲存每個答案。 map <string,int> mm;//儲存每種答案出現的次數。 void part_1(){ for(int i=0;i<n;i++){//順序枚舉。 if(mm[st[i]]==p){ //假如當前答案的出現數等於正解數, for(int j=0;j<m;j++){//ch中是這個答案的錯排。 if(st[i][j]=='N') ch[j]='Y'; else ch[j]='N'; } if(mm[ch]==q){cout<<st[i]<<endl;return;}//並且它的錯排的出現數等於報零答案數,可以認為這是正解。 } } printf("-1 ");//如果沒有找到,即p,q不合法,輸出負一。 } void part_2(){ for(int i=0;i<n;i++){ if(mm[st[i]]==q){//如果當前答案的出現數等於報零答案數, for(int j=0;j<m;j++){ if(st[i][j]=='N') ch[j]='Y'; else ch[j]='N'; } if(!mm[cn]){cout<<ch<<endl;return;}//並且它的錯排,即正解,沒有出現過,則它的錯排合法。 } } printf("-1 ");//對於數據,並無意義。 } void part_3(){//正解和報零答案都沒有出現過,所以要捋一下全排列。 for(int i=0;i<m;i++){ cn[i]='N';//從字典序排列時最前面的先拿出。 z*=2;//z等於二的m次冪。 } for(int i=0;i<z/2;i++){//小於z可以理解,因為這是一個類似01字串的字符串。 a=m-1;cn[a]+='Y'-'N'; while(cn[a]==2*'Y'-'N'&&a>0){ cn[a--]='N'; cn[a]+='Y'-'N'; }//因為有錯排,所以是小於z/2。 for(int j=0;j<m;j++){ if(cn[j]=='N') ch[j]='Y'; else ch[j]='N'; } if(!mm[cn]&&!mm[ch]){cout<<cn<<endl;return;}//如果該排列即其錯排都為出現個,則該排列合法。 } printf("-1 ");//對於數據,並無意義。 } int main(){ freopen("answer.in","r",stdin); freopen("answer.out","w",stdout); scanf("%d%d%d%d",&n,&m,&p,&q); for(int i=0;i<n;i++){ scanf("%s",&ch);//一種比較簡單的讀入技巧,時間大概是直接cin流輸入的三分之一。 for(int j=0;j<m;j++) st[i]+=ch[j]; ++mm[st[i]];//記錄重新次數。 } sort(st,st+n);//因為答案要按字典序輸出,所以排一下序。 if(p) part_1(); if(!p&&q) part_2(); if(!p&&!q) part_3(); return 0; }
吐槽一下,這個答案很多時候并並不正確。