A Digits Are Not Just Characters
- 给出n个字符串si和一个base串s0,问si和s0的大小关系。
比较的时候,先将s分割成数字串和字符串,然后对应去比较。
#include <bits/stdc++.h>
using namespace std;
bool isdig(char c){
return c>='0'&&c<='9';
}
int check1(string s1, string s2){
int num1 = 0, num2 = 0;
string str1, str2;
if(isdig(s1[0])){
num1 = atoi(s1.c_str());
}
else {
str1= s1;
}
if(isdig(s2[0])){
num2 = atoi(s2.c_str());
}else str2 = s2;
if(num1 || num2){
if(num1==0) return 1;
if(num2==0) return -1;
if(num1==num2)return 0;
return num1<num2?-1:1;
}
if(str1==str2)return 0;
return str1<str2?-1:1;
}
vector<string> parse(string s){
vector<string> ret;
string tmp;
for(int i = 0; i< s.size();++i){
if(i==0 || (isdig(s[i-1])^isdig(s[i]))){
if(tmp.size()) ret.push_back(tmp);
tmp = s[i];
}
else {
tmp += s[i];
}
}
if(tmp.size()) ret.push_back(tmp);
return ret;
}
bool check(string s0, string s1){
vector<string> ss1 = parse(s0);
vector<string> ss2 = parse(s1);
int n = ss1.size(), m = ss2.size();
for(int i = 0; i < min(n,m); ++i){
int ret = check1(ss1[i],ss2[i]);
if(ret==0)continue;
return ret<0;
}
return n<m;
}
int main(){
int n;
cin>>n;
string s0;
cin>>s0;
for(int i = 0; i < n; ++i){
string s;
cin>>s;
if(check(s,s0))printf("-\n");
else printf("+\n");
}
}
B Arithmetic Progressions
- 求一个最长的等差数列
枚举i,j为等差数列的最后两个,然后dp[i][j] = dp[j][(2*j-i)]+1
#include <bits/stdc++.h>
using namespace std;
int dp[5010][5010];
int a[5010];
int main(){
memset(dp,0,sizeof(dp));
int n;
cin>>n;
map<int,int> pos;
for(int i = 0; i < n; ++i){
cin>>a[i];
}
sort(a,a+n);
int ans = 2;
for(int i = 0; i < n; ++i){
dp[i][i] = 1;
for(int j = i-1; j>=0;--j){
dp[i][j] = 2;
int pre = 2*a[j]-a[i];
if(pos.find(pre)==pos.end())continue;
dp[i][j] = max(dp[i][j], dp[j][pos[pre]]+1);
ans = max(ans, dp[i][j]);
}
pos[a[i]]=i;
}
cout<<ans<<endl;
}
C Emergency Evacuation
- 给出一个n*m的格子图,然后图上的人都要跑出去,每一个格子不能同时站两个人,问最短时间
由于出口只有一个,因此反过来算从出口进去回到原来位置的最短时间。
按照距离排序,那么每个人的时间就是等待时间加上回去的距离。进去之后前面的肯定不会卡住后面的。
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<pair<int,int>> x;
int r,s,p;
scanf("%d%d%d",&r,&s,&p);
for(int i = 0; i < p; ++i){
int a,b;
scanf("%d%d",&a,&b);
if(b>s)b = 2*s +1 - b;
a = r+1-a;
x.push_back(make_pair(a,b));
}
auto cmp = [&](const pair<int,int> &a, const pair<int,int> &b){
int dis1 = (s-a.second+1) + a.first;
int dis2 = (s-b.second+1) + b.first;
return dis1>dis2;
};
sort(x.begin(),x.end(),cmp);
int ans =0;
for(int i = 0; i < p; ++i){
int dis = (s-x[i].second+1) + x[i].first;
ans = max(ans, dis + i);
}
cout<<ans<<endl;
}
D Shortest Common Non-Subsequence
- 求一个最短串,不是A,B两个的子串
其实题目就是求一个最短的串,然后能跑满A,B两个串。
dp[i][j]表示在A的第i位B的第j位开始匹配,匹配到最后时的最短距离。
那么有两种选择,去0或者去1.转移就是dp[i][j] = min(dp[next[i][0]][next[j][0]], dp[next[i][1]][next[j][1]])+1
这样就可以求出从00开始的最短串了。然后构造这个最短串,末尾加个0就是答案了。(其实这个0不用加,直接跑到两个串末尾就自动加上去了)
#include <stdio.h>
#include <string>
#include <string.h>
#include <iostream>
using namespace std;
int dp[4010][4010];
int nt1[4010][2],nt2[4010][2];
int path[4010][4010];
int len1,len2;
int get_min(int x, int y){
if(x==-1) return y;
if(y==-1) return x;
return min(x,y);
}
string ans;
void get_ans(int i, int j){
if(i==len1+1&&j==len2+1){
return;
}
ans+= path[i][j] + '0';
get_ans(nt1[i][path[i][j]],nt2[j][path[i][j]]);
}
int main(){
string s1,s2;
cin>>s1>>s2;
if(s1.size() > s2.size()) swap(s1,s2);
len1 = s1.size(), len2 = s2.size();
nt1[len1+1][0] = nt1[len1+1][1] = len1+1;
nt2[len2+1][0] = nt2[len2+1][1] = len2+1;
for(int i = len1;i>=0;--i){
nt1[i][0] = nt1[i+1][0];
nt1[i][1] = nt1[i+1][1];
nt1[i][s1[i]-'0'] = i+1;
}
for(int i = len2; i>=0;--i){
nt2[i][0] = nt2[i+1][0];
nt2[i][1] = nt2[i+1][1];
nt2[i][s2[i]-'0']=i+1;
}
dp[len1+1][len2+1]=0;
for(int i = len1+1; i>=0;--i){
for(int j = len2+1; j >=0;--j){
if(i==len1+1&&j==len2+1)continue;
int ret1 = dp[nt1[i][0]][nt2[j][0]]+1;
int ret2 = dp[nt1[i][1]][nt2[j][1]]+1;
if(ret1<=ret2){
dp[i][j] = ret1;
path[i][j] = 0;
}
else {
dp[i][j] = ret2;
path[i][j] = 1;
}
}
}
get_ans(0,0);
cout<<ans<<endl;
}
F What Goes Up Must Come Down
- 给出一个n个元素的数组,可以交换相邻的两个数,问至少需要交换多少次,使得数组是单峰的。
最小的那个数肯定是在数组的最左或者最后,那么把最小的那个移到边缘之后,就变成了一个子问题了。
因此从小打到枚举数,判断去左还是去右。
#include <bits/stdc++.h>
using namespace std;
int sum[100010], lt[100010],rt[100010];
int a[100010];
int max_n;
void add(int x){
for(;x<=max_n;x+=x&-x)sum[x]++;
}
int get_sum(int x){
int ret = 0;
while(x>0){
ret += sum[x];
x-=x&-x;
}
return ret;
}
int find(int x){
return get_sum(max_n)-get_sum(x);
}
int main(){
int n;
scanf("%d",&n);
max_n = 0;
for(int i = 1; i<= n; ++i){
scanf("%d",&a[i]);
max_n = max(max_n,a[i]);
}
memset(sum,0,sizeof(sum));
for(int i = 1; i<=n; ++i){
int pos = find(a[i]);
lt[i] = pos;
add(a[i]);
}
vector<int> pos;
memset(sum,0,sizeof(sum));
for(int i = n; i>0; --i){
int ps = find(a[i]);
rt[i] = ps;
add(a[i]);
pos.push_back(i);
}
long long ans = 0;
for(int idx = 0; idx < pos.size();++idx){
int i = pos[idx];
ans += min(lt[i],rt[i]);
}
cout<<ans<<endl;
}
H Colorful Tree
- 给出一棵树,每个节点对应一种颜色。有m个询问,修改节点的颜色或者对于某一种颜色,包含这些颜色的子树有多少条边。
直接求每一种颜色的树的大小有一丢丢难(至少我没想到)。那么就只能一个一个的加到对应的颜色里面,算他的贡献了。
加入一个节点时,其他颜色的节点只有两种情况,要不在子树,要不在父亲,
如果所有点都在子树或者在父亲,那么就是到最远和最近的父亲或者子树u,贡献就是(dis(x,u)+dis(x,v)-dis(u,v))/2。
否则贡献为0,因为子树到父亲必定经过这个点。
这个两种其实可以合在一起,父亲是u,子树是v,那么贡献就是(dis(x,u)+dis(x,v)-dis(u,v))/2。
因此添加或者删除的时候通过dfs序找出最近的父亲(dfs序-1)或者子树(dfs序+1),然后加上或者减掉贡献就好了。
using namespace std;
int mod = 998244353;
//int mod = 1e9+7;
vector<int> g[N];
int idx;
int dfn[N],dfn_c[N];
int c[N],num[N];
int fa[N][32], dp[N];
int n;
set<int> pos[N];
void dfs(int t, int f){
dfn[t] = ++idx;
dfn_c[idx] = t;
fa[t][0] = f;
dp[t] = dp[f]+1;
for(int i = 1; i < 20; ++i){
fa[t][i] = fa[fa[t][i-1]][i-1];
}
for(int u : g[t]){
if(u==f)continue;
dfs(u,t);
}
}
int lca(int u, int v){
if(dp[u] < dp[v]) swap(u,v);
for(int i = 19; i >=0;--i){
if(dp[fa[u][i]] >= dp[v]){
u = fa[u][i];
}
}
if(u==v) return u;
for(int i = 19; i >=0;--i){
if(fa[u][i]!=fa[v][i]){
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
}
int get_dis(int u, int v){
int f = lca(u,v);
return dp[u] + dp[v] - 2*dp[f];
}
int get_dis(int u, int v, int x){
int dis1 = get_dis(u,x);
int dis2 = get_dis(v,x);
int dis3 = get_dis(u,v);
return (dis1+dis2 - dis3)/2;
}
void change_dis(int idx, int flag){
int col = c[idx];
if(pos[col].empty()){
num[col]=0;
return;
}
auto r = pos[col].lower_bound(dfn[idx]);
if(r==pos[col].begin() || r == pos[col].end()){
auto l = pos[col].begin();
auto r = pos[col].rbegin();
num[col] += get_dis(dfn_c[*l], dfn_c[*r], idx) * flag;
}
else {
auto l = r; --l;
num[col] += get_dis(dfn_c[*l], dfn_c[*r], idx) * flag;
}
}
void add(int idx, int flag){
int col = c[idx];
if(flag==1){
change_dis(idx,flag);
pos[col].insert(dfn[idx]);
}
else {
pos[col].erase(dfn[idx]);
change_dis(idx,flag);
}
}
int main(){
clr(num);
cin>>n;
idx = 0;
for(int i = 0; i < n-1; ++i){
int a,b;
cin>>a>>b;
g[a].pb(b);
g[b].pb(a);
}
dfs(1,0);
for(int i = 1; i<=n; ++i){
cin>>c[i];
add(i,1);
}
int m;
cin>>m;
for(int i = 0; i < m; ++i){
char op;
int idx;
cin>>op>>idx;
if(op=='Q') {
if(pos[idx].empty()) cout<<-1<<endl;
else cout<<num[idx]<<endl;
}
else {
int new_c;
cin>>new_c;
add(idx,-1);
c[idx]=new_c;
add(idx,1);
}
}
}
I Sixth Sense
- 给出两个序列a,b。对b进行调整使得
最大。如果有多个b,选字典序最大的。
最大值可以通过贪心。对于a[i],upper_bound出b。如果a是有序的话,b的结果是单调的。因此可以通过指针移动来解决。复杂度O(n)
然后在选择字典序最大时,枚举每一个a可以选择的b,然后去看剩下的a和b是否能满足条件。
枚举b需要二分,对比a大和比a小分别讨论,因此比a大贡献为1.
枚举时,先构造出可以用的a和b,这样时间就是O(n),在枚举加检查就是O(nlog(n))
总时间就是O(n^2log(n))
int n;
int p[N],f[N];
int vb[N];
int get_ans(vector<int>& a, vector<int>& b){
int num = 0;
int pos = 0;
for(int i = 0; i < a.size(); ++i){
while(pos<b.size()&&(vb[pos] || b[pos]<=a[i]))pos++;
if(pos<b.size()){
num++;
pos++;
}
}
return num;
}
int find(vector<int>&a, vector<int>&b, int s_idx, int e_idx, int tot){
int l = s_idx, r = e_idx;
while(l+1<r){
int mid = (l+r)>>1;
vb[mid]=1;
int tmp = get_ans(a,b);
if(tmp >= tot){
l = mid;
}
else {
r = mid;
}
vb[mid]=0;
}
return l;
}
int main(){
cin>>n;
vector<int> a,b;
vector<pair<int,int>> pa;
fr(i,0,n){
cin>>p[i];
pa.pb(mp(p[i],i));
}
fr(i,0,n){
cin>>f[i];
b.pb(f[i]);
}
sort(pa.begin(), pa.end());
sort(b.begin(), b.end());
for(int j = 0; j < pa.size();++j){
a.pb(pa[j].first);
}
int tot = get_ans(a,b);
vector<int> ans;
fr(i,0,n){
vector<int> a;
for(int j = 0; j < pa.size();++j){
if(pa[j].second<=i)continue;
a.pb(pa[j].first);
}
vector<int> tmp;
for(int j = 0; j < b.size();++j){
if(vb[j])continue;
tmp.pb(b[j]);
}
b = tmp;
for(int j = 0; j < b.size();++j){
vb[j] = 0;
}
int pos = upper_bound(b.begin(), b.end(), p[i])-b.begin();
int ret = find(a,b,pos,b.size(),tot-1);
if(ret!=b.size()){
vb[ret]=1;
if(get_ans(a,b)==tot-1){
ans.pb(b[ret]);
tot--;
continue;
}
vb[ret]=0;
}
ret = find(a,b, 0,pos, tot);
vb[ret]=1;
ans.pb(b[ret]);
}
for(int x : ans)pf("%d ",x);
pf("\n");
}
J Jokewithpermutation
- 给出一个字符串,他是一个n的排列组成的串。还原他的排列。
暴力搜就好了。
#include <bits/stdc++.h>
using namespace std;
int num[100];
string s;
int n;
vector<int> ans;
bool dfs(int idx){
if(idx>=n){
int max_v = 0;
for(int i = 0; i < ans.size();++i){
max_v = max(max_v, ans[i]);
}
for(int i = 1; i <=max_v; ++i){
if(num[i]==0) return 0;
}
return 1;
}
int ret = 0;
for(int i = 0; i < 2; ++i){
ret = ret * 10 + s[idx+i]-'0';
if(num[ret]==0){
num[ret]=1;
ans.push_back(ret);
if(dfs(idx+i+1))return 1;
num[ret]=0;
ans.pop_back();
}
}
return 0;
}
int main(){
cin>>s;
n = s.size();
dfs(0);
for(int x : ans) printf("%d ",x);printf("\n");
}