A. Angry Students
A |
题意:一排学生打雪仗,A是生气的人,P是文静的人,每回合A会影响后面的一个P使他也生气,如PAPP一回合之后是PAAP,求第几回合之后生气人数不再增加
题解:语法题,统计A后面连续的P的最大值即可
时间复杂度:O(n)
B. Hyperset
B |
题意:给定n,m,n张牌每次选三张,m种属性对应一个游戏规则,在三种属性:‘S’,‘E’,‘T’,要么三种牌都相同,要么三张牌都不相同,求选择的方案数
题解:注意数据量,1500允许n^2算法,题目就简单了,三张牌循环两张,借用规则求出应该有的第三张,在所有牌中查询,注意最后(1)牌相同(2)可能性是轮换的要除以3。
时间复杂度:两重循环n^2,map查询logn,总时间复杂度O(k*n^2*logn)
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
using namespace std;
const int N = 100100;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
typedef pair<int,int> PII;
typedef long long ll;
int n,m,d;
string s[10010];
map<string,int> mp;
int main(void){
cin>>n>>m;
for(int i = 1;i <= n;i++){
cin>>s[i];
mp[s[i]]++;
}
int ans = 0;
for(int i = 1;i < n;i++){
for(int j = i+1;j <= n;j++){
string news;
for(int k = 0;k < m;k++){
if(s[i][k] == 'S' && s[j][k] == 'E')news += 'T';
else if(s[i][k] == 'S' && s[j][k] == 'T')news += 'E';
else if(s[i][k] == 'E' && s[j][k] == 'S')news += 'T';
else if(s[i][k] == 'E' && s[j][k] == 'T')news += 'S';
else if(s[i][k] == 'T' && s[j][k] == 'S')news += 'E';
else if(s[i][k] == 'T' && s[j][k] == 'E')news += 'S';
else if(s[i][k] == s[j][k])news += s[i][k];
}
if(news == s[i] && news == s[j])ans += (mp[s[i]]-2);
else if((news == s[i] && news != s[j]) || (news != s[i] && news == s[j]))
ans += (mp[s[i]]-1);
else ans += mp[news];
}
}
cout<<ans/3<<endl;
return 0;
}
C. Garland
C |
题意:在一个排列中缺少一些数,用0表示空位,你可以填缺失的数,最后的权值计算规则是从头遍历,每改变一次奇偶性权值最小,求填补后最小权值
题解:注意到数据量100,最高允许O(n^3),考虑贪心,每次对已经存在的奇数偶数前面填写奇偶性相同的数字,但是会有问题,比如:剩余2个奇数时 2 0 0 0 3 0 0 5 0 0 0 7,显然将两个奇数填写在中间是最好的,于是用更全面的做法:DP[pos][odd][even][2],pos表示位置,odd表示剩余奇数元素数,even表示剩余偶数元素数,1表示前一个数状态是奇数,0是偶数(从前一个状态进行状态转移)
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
using namespace std;
const int N = 100100;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
typedef pair<int,int> PII;
typedef long long ll;
int n,m,d;
int dp[101][101][101][2];//pos,剩余oddnum,剩余evennum,上一位状态
int q[110];
int main(void){
cin>>n;
int oddnum = n+1>>1;
int evenum = n>>1;
for(int i = 1;i <= n;i++){
cin>>q[i];
if(q[i]){
if(q[i]&1)oddnum--;
else evenum--;
}
}
memset(dp,0x3f,sizeof dp);
dp[0][oddnum][evenum][0] = dp[0][oddnum][evenum][1] = 0;
for(int i = 1;i <= n;i++){
for(int j = 0;j <= oddnum;j++){///奇数
for(int k = 0;k <= evenum;k++){
if(!q[i]){///1是上一位奇数,0是上一位偶数
dp[i][j][k][0] = min(dp[i-1][j][k+1][0],dp[i-1][j][k+1][1]+1);
dp[i][j][k][1] = min(dp[i-1][j+1][k][0]+1,dp[i-1][j+1][k][1]);
}
else{
if(q[i]&1){
dp[i][j][k][1] = min(dp[i-1][j][k][0]+1,dp[i-1][j][k][1]);
}
else{
dp[i][j][k][0] = min(dp[i-1][j][k][0],dp[i-1][j][k][1]+1);
}
}
}
}
}
cout<<min(dp[n][0][0][0],dp[n][0][0][1]);
return 0;
}
D. Numbers on Tree
D |
题意:给定n个点的树和n个点的关系,下面n行代表关系,每个关系给出两个数字,前一个是该行的点的父亲节点是谁,后一个是子树中点权小于该点的点的数量,根据前一个可以知道树的形状,后一个可以知道树的子树的点权关系,现在树的每个节点权未知,问是否可以复原该树,可以按照点的顺序给出点权方案
题解:显然可以从子节点向上看,对整棵树进行填数,数据规模2000,支持O(n^2)算法,显然有解的话,可以所有点的排列(这一点请理解)最开始的想法是将树根据点权c[x],每次从一个集合中选取第c[x]+1的元素,如果点权是0,就选最小元素。但是WA16,不太理解,应该是点权是0的地方出现问题。所以改变思路,对于每个节点,子树填数,比如该节点子树有10个点,c[x]是5(有五个节点比这个节点的权小),那么根据bfs回溯,先把子节点确定,返回前四个整合好的vector,然后该点因为c[x]是5,选vector前5个较小的,添加自己的位置,再把后面的位置填数,比如:
(?)c[?]=5
5 2 3 1 4 x x x x x
前五个确定好位置,然后根节点存编号进vector中,后面依次把位置7,8,9,10,11的编号填进去
(注意理解,返回的vector存的是每个点的编号,而位置才是真正的数,利用了vector增长时候新增加的点会是所有位置+1的性质)怎么判断错误?一个点的子树返回的vector不满足c[i],比如c[i]是100,子节点只有50个点,要比100个点大,显然不成立,exit(0)
tips.这题学会了用vector存树,比用链式前向星方便说实话
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int N = 100100;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
typedef pair<int,int> PII;
typedef long long ll;
vector<int> e[N];
int n,c[N],p[N],a[N];
vector<int> dfs(int u){
vector<int> ans;
for(int i = 0;i < e[u].size();i++){
vector<int> tmp = dfs(e[u][i]);
for(int j = 0;j < tmp.size();j++){
ans.push_back(tmp[j]);
}
}
if(u == 0) return ans;
if(ans.size() < c[u]){
cout<<"NO\n";
exit(0);
}
vector<int> newans;
for(int i = 0;i < c[u];i++) newans.push_back(ans[i]);
newans.push_back(u);
for(int i = c[u];i < ans.size();i++) newans.push_back(ans[i]);
swap(ans,newans);
return ans;
}
int main(void){
cin>>n;
for(int i = 1;i <= n;i++){
cin>>p[i]>>c[i];
e[p[i]].push_back(i);
}
vector<int>ans = dfs(0);
cout<<"YES\n";
for(int i = 0;i < ans.size();i++) a[ans[i]] = i+1;
for(int i = 1;i <= n;i++)cout<<a[i]<<' ';
cout<<endl;
return 0;
}
E1. Madhouse(Easy version)
交互题(QAQ1)
E1 |
题意:这里有一串长度为n的字符串,每次询问一个区间,会给出这个区间的所有连续子串,问每次询问返回最多(n+1)/2个,请输出该字符串
题解:注意到n(n+1),正好是长度为n的所有连续子串数量,可以第一次询问(1,n),然后询问(2,n),比如abc
1~3 a,b,c,ab,bc,abc
2~2 b,c,bc
然后每一次根据长度减,比如abc减去bc得到第一个元素a,再对第二位进行运算,最后输出答案
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include <vector>
using namespace std;
const int N = 110;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
typedef pair<int,int> PII;
typedef long long ll;
int n;
map<string,int> mp1[N],mp2[N];
char getChar(string a,string b){
map<char,int> tmp;
for(int i = 0;i < a.size();i++) tmp[a[i]]++;
for(int i = 0;i < b.size();i++) tmp[b[i]]--;
for(auto it:tmp)if(it.second)return it.first;
}
int main(void){
cin>>n;
vector<char> ans;
if(n == 1){
cout<<"? 1 1\n";
string tmp;cin>>tmp;
cout<<"! "<<tmp<<"\n";
return 0;
}
cout<<"? 1 "<<n<<endl;
for(int i = 1;i <= n*(n+1)/2;i++){
string tmp;cin>>tmp;
sort(tmp.begin(),tmp.end());
mp1[tmp.size()][tmp]++;
}
cout<<"? 2 "<<n<<endl;
for(int i = 1;i <= n*(n-1)/2;i++){
string tmp;cin>>tmp;
sort(tmp.begin(),tmp.end());
mp2[tmp.size()][tmp]++;
}
for(int i = 1;i <= n;i++){///长度为i的string的mp
for(auto it2: mp2[i]){///将mp1中减去,得到剩余项
mp1[i][it2.first] -= mp2[i][it2.first];
}
for(auto it1: mp1[i]){
if(it1.second){
string tmp = "";
for(int j = 0;j < ans.size();j++) tmp += ans[j];
// cout<<"%%%% "<<it1.first<<' '<<tmp<<endl;
// cout<<"###"<<getChar(it1.first,tmp)<<endl;
ans.push_back(getChar(it1.first,tmp));
}
}
}
cout<<"! ";
for(int i = 0;i < ans.size();i++) cout<<ans[i];
return 0;
}
/*
4
a
a
c
b
aa
ac
cb
aac
acb
aacb
a
c
b
ac
cb
acb
*/
E2. Madhouse(Hard version)
E2 |
题意:与上一题没有区别,但是询问的次数少了不少,是0.77(n+1)^2,所以直接问1~n不可行了
题解:这题因为返回的字符串数量较少,于是考虑增加询问得到完整字符串,(1,n/2)(1,n/2+1)(1,n)
通过分解因式可以得到这里总共有多少子串,大概是0.75的n多项式,题目要求查询次数小于0.77的n多项式,满足
根据(1,n/2)与(1,n/2+1)之间差值得到前面n/2个数
比如abcde
长度1:a,b,c,d,e,f 出现一次
长度2:ab,bc,cd,de,ef 除了首尾均出现2次,得到首尾元素,然后因为前面已经知道前面n/2个元素,所以得到首尾元素
长度3:abc,bcd,cde 减去长度为2的,这里首尾未知的是ab,de,因为前面已经推出了a,e,且前面n/2个元素已知,所以得到ab然后推出de
长度4,5:同上处理
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include <vector>
using namespace std;
const int N = 110;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
typedef pair<int,int> PII;
typedef long long ll;
int nn;
map<string,int> mp1[N],mp2[N];
int mps[30][10010];
char getChar(string a,string b){
map<char,int> tmp;
for(int i = 0;i < a.size();i++) tmp[a[i]]++;
for(int i = 0;i < b.size();i++) tmp[b[i]]--;
for(auto it:tmp)if(it.second)return it.first;
}
string halfsolve(int n){
vector<char> ans;
cout<<"? 1 "<<n<<endl;
for(int i = 1;i <= n*(n+1)/2;i++){
string tmp;cin>>tmp;
sort(tmp.begin(),tmp.end());
mp1[tmp.size()][tmp]++;
}
cout<<"? 2 "<<n<<endl;
for(int i = 1;i <= n*(n-1)/2;i++){
string tmp;cin>>tmp;
sort(tmp.begin(),tmp.end());
mp2[tmp.size()][tmp]++;
}
for(int i = 1;i <= n;i++){///长度为i的string的mp
for(auto it2: mp2[i]){///将mp1中减去,得到剩余项
mp1[i][it2.first] -= mp2[i][it2.first];
}
for(auto it1: mp1[i]){
if(it1.second){
string tmp = "";
for(int j = 0;j < ans.size();j++) tmp += ans[j];
ans.push_back(getChar(it1.first,tmp));
}
}
}
string tt;
for(int i = 0;i < ans.size();i++) tt += ans[i];
return tt;
}
int main(void){
cin>>nn;
vector<char> ans;
if(nn == 1){
cout<<"? 1 1\n";
string tmp;cin>>tmp;
cout<<"! "<<tmp<<"\n";
return 0;
}
else if(nn == 2){
cout<<"? 1 1\n";
string tmp1,tmp2;
cin>>tmp1;
cout<<"? 2 2\n";
cin>>tmp2;
cout<<"! "<<tmp1<<tmp2<<endl;
return 0;
}
string halfans = halfsolve(nn+1>>1);///得到前一半的字母序列(消耗n^2/2)
cout<<"? 1 "<<nn<<endl;
for(int i = 0;i < nn*(nn+1)/2;i++){///再询问一次全部的得到全部序列
string tmp;cin>>tmp;
for(int j = 0;j < tmp.size();j++){
mps[tmp[j]-'a'][tmp.size()]++;///字母 长度层数
}
}
string anti_halfans = "";
for(int i = 1;i <= nn/2;i++){///遍历前一半每一位
for(int j = 0;j < 26;j++){
int num = mps[j][1]-(mps[j][i+1]-mps[j][i]);
for(int k = 0;k < i;k++){
if(halfans[k]-'a' == j)num--;///减去前面出现多少次
}
for(int k = 0;k < anti_halfans.size();k++){///减去后面出现次数
if(anti_halfans[k]-'a' == j)num--;
}
if(num > 0){
anti_halfans += ('a'+j);
break;
}
}
}
// cout<<"## "<<halfans<<' '<<anti_halfans<<endl;
for(int i = 0;i < anti_halfans.size();i++){
halfans += anti_halfans[anti_halfans.size()-1-i];
}///正序与逆序合并
cout<<"! "<<halfans;
return 0;
}
/*
4
a
b
ab
b
a
b
c
d
ab
bc
cd
abc
bcd
abcd
*/