pog loves szh I
Accepts: 497
Submissions: 822
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
问题描述
pog拥有很多字符串,它喜欢将两个长度相等字符串交错拼在一起,如abcd与efgh,那么交错拼在一起就成了aebfcgdh啦! szh觉得这并不好玩,因此它将第二个字符串翻转了一遍,如efgh变成了hgfe,然后再将这两个字符串交错拼在一起,因此abcd与efgh就成了ahbgcfde啦! 现在问题来了,pog手里有一个由szh亲手拼好的字符串,它想还原出原来的两个字符串,出于szh对pog的爱,szh帮助pog还原出了原来的两个字符串。
输入描述
第一行读入一个整数 T(1≤T≤100) ,表示有 T 组数据。 接下来 T 行每行读入一个长度为偶数且仅包含英文小写字母的字符串 (|S|≤100) 。
输出描述
对于每组数组,输出两行,表示原来的两个字符串。
输入样例
1 aabbca
输出样例
abc aba
Hint
本题在hack时行末需要换行水题不解释
#include<bits/stdc++.h>
using namespace std;
string ans1,ans2,a;
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>a;
ans1="",ans2="";
for(int i=0;i<a.size();i++)
{
if((i&1)==0)ans1+=a[i];
else ans2+=a[i];
}
reverse(ans2.begin(),ans2.end());
cout<<ans1<<endl<<ans2<<endl;
}
return 0;
}
pog loves szh II
Accepts: 97
Submissions: 834
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
思路:首先每个数对P取模,然后排序,那么任意两个数相加,要么小于p,要么小于2*p,因为我们找尽量大的,所以对于x这个数,我们找p-x-1或者2*p-x-1是不是存在,二分一下就行了,要注意的是两个数的位置不能重叠
下面是官方发布的题解,找的时候不用二分,直接双指针扫一遍
由于序列中的数可能超过 P ,所以将所有的数读入后进行取模操作。之后将取模后的所有数从小到大排序。题目要求我们求不同位置的两个数的和在取模意义下的最大值,而现在所有数都是小于 P 且排好序的。因此设我任意选了两个数是 X 和 Y ,显然 0≤X+Y≤2P−2 。若 X+Y<P ,则这次选数的答案就是 X+Y ,若 X+Y≥P ,则答案是 X+Y−P 。 那么我们可以这样做:将其中最大的两个数字相加取模,设为一个可能的答案记录在ANS中。这个答案是第二种情况的最大值。再对排序好的序列进行枚举,对每个枚举到的数,找出最大的数,使这个数与枚举到的数相加后的和最大且小于P,将之记为可能的答案并于之前找到的最大值ANS进行比较。这个答案是第一种情况中的可能的最大值。而普通的枚举是要超时的,但是我们发现如果从小到大枚举第一个数,那么另一个匹配的数显然是从大到小的,因此可以用一个NOW记录当前另一个匹配的数的位置,每次枚举时NOW递减至符合条件。可以做到 O(n) 的时间复杂度。 综上所述,时间复杂度为快速排序的 O(nlogn) ,空间复杂度为 O(n) 。注意一些特殊情况如同一个位置不能多次选。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=100010;
int N;
LL a[maxn],P;
map<LL,int> mp;
void solve()
{
LL ans=0;
for(int i=1;i<=N;i++)
{
LL x=a[i];
int pos=lower_bound(a+1,a+1+N,P-1-x)-a;
if(pos>N)pos=N;
if(a[pos]>P-x-1)pos--;
if(pos>0&&(pos!=i||mp[x]>1))ans=max(ans,(x+a[pos])%P);
pos=lower_bound(a+1,a+1+N,P*2-x-1)-a;
if(pos>N)pos=N;
if(a[pos]>2LL*P-x-1)pos--;
if(pos>0&&(pos!=i||mp[x]>1))ans=max(ans,(x+a[pos])%P);
}
printf("%I64d\n",ans);
}
int main()
{
while(scanf("%d%I64d",&N,&P)!=EOF)
{
mp.clear();
for(int i=1;i<=N;i++)
{
scanf("%I64d",&a[i]);
a[i]%=P;
mp[a[i]]++;
}
sort(a+1,a+1+N);
N=unique(a+1,a+1+N)-a-1;
solve();
}
return 0;
}
pog loves szh III
Accepts: 63
Submissions: 483
Time Limit: 12000/6000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
pog在与szh玩游戏,首先pog在纸上画了一棵有根树,这里我们定义1为这棵树的根,然后szh在这棵树中选了若干个点,想让pog帮忙找找这些点的最近公共祖先在哪里,一个点 为S的最近公共祖先当且仅当以该点为根的子树包含S中的所有点,且该点深度最大。然而,这个问题是十分困难的,出于szh对pog的爱,他决定只找编号连续的点,即 li ~ ri 。
输入描述
若干组数据(不超过 3 组 n≥10000 或 Q≥10000 )。 每组数据第一行一个整数 n(1≤n≤300000) ,表示树的节点个数。 接下来 n−1 行,每行两个数 Ai,Bi ,表示存在一条边连接这两个节点。 接下来一行一个数 Q(1≤Q≤300000) ,表示有 Q 组询问。 接下来Q行每行两个数 li,ri(1≤li≤ri≤n) ,表示询问编号为 li ~ ri 的点的最近公共祖先。
输出描述
对于每组的每个询问,输出一行,表示编号为li~ri的点的最近公共祖先的编号。
输入样例
5 1 2 1 3 3 4 4 5 5 1 2 2 3 3 4 3 5 1 5
输出样例
1 1 3 3 1
Hint
珍爱生命,远离爆栈。
思路:对于区间(l,r)的最近公共祖先,是其中dfs序最小的和最大的两个数的最近公共祖先,因为最小的说明是最早的一个分支,反之是最大的,所以他两个的最近公共祖先可以代表整个区间的。那么这个问题就变得很简单了,首先dfs把该标记的都标记了,然后初始化RMQ(用来查询区间dfs序最大最小),询问的时候只需要查出区间最大最小的是谁,然后求这两个的LCA
下面是官方给的题解:
做这题的方法有很多。下面给出2种解法。 1:维护一个跳表,表示编号为 i ~ i+2j−1 的LCA,注意在这里求LCA必须用 O(1) 的做法才能通过所有数据。可以转换为RMQ,每次查询时只需查询两个数的LCA即可。 2:考虑dfs序,通过在简单的证明可知L~R的LCA为 L ~ R 中dfs序较小的那个位置与dfs序较大的那个位置的LCA。因此只要通过st表处理L~R最大dfs序与最小dfs序的编号即可。 另外本题可能会出现爆栈的问题,可以通过黑科技避免,或者用bfs来代替dfs,用bfs维护dfs序的方法比较简单不再阐述。 复杂度为 O(nlgn)
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
const int maxn=300010;
int N,Q,dfs_clock;
int a[maxn];
int d1[maxn][20],d2[maxn][20];
int dep[maxn],fa[maxn],anc[maxn][20];
int num[maxn],cnt[maxn];
vector<int> G[maxn];
void dfs1(int u,int f,int depth)
{
cnt[u]=++dfs_clock;
dep[u]=depth;
fa[u]=f;
num[u]=1;
int len=G[u].size();
for(int i=0;i<len;i++)
{
int v=G[u][i];
if(v==f)continue;
dfs1(v,u,depth+1);
num[u]+=num[v];
fa[v]=u;
}
}
void preprocess()
{
//处理最近公共祖先的RMQ
for(int i=1;i<=N;i++)
{
anc[i][0]=fa[i];
for(int j=1;(1<<j)<=N;j++)
anc[i][j]=-1;
}
for(int j=1;(1<<j)<=N;j++)
{
for(int i=1;i<=N;i++)
{
if(anc[i][j-1]==-1)continue;
int a=anc[i][j-1];
anc[i][j]=anc[a][j-1];
}
}
//处理dfs序的RMQ
for(int i=0;i<=N;i++)
d1[i][0]=d2[i][0]=i;
for(int j=1;(1<<j)<=N;j++)
for(int i=1;i+(1<<j)<=N+1;i++)
{
int x=d1[i][j-1],y=d1[i+(1<<(j-1))][j-1];
if(cnt[x]<=cnt[y])d1[i][j]=x;
else d1[i][j]=y;
x=d2[i][j-1],y=d2[i+(1<<(j-1))][j-1];
if(cnt[x]>=cnt[y])d2[i][j]=x;
else d2[i][j]=y;
}
}
//询问最近公共祖先
int LCA(int p,int q)
{
if(dep[p]<dep[q])swap(p,q);
int k=0;
while((1<<(k+1))<=dep[p])k++;
for(int i=k;i>=0;i--)
if(dep[p]-(1<<i)>=dep[q])
p=anc[p][i];
if(p==q)return p;
for(int i=k;i>=0;i--)
if(anc[p][i]!=-1&&anc[p][i]!=anc[q][i])
p=anc[p][i],q=anc[q][i];
return fa[p];
}
//询问dfs序最小的跟最大的
void query(int l,int r,int &x,int &y)
{
int k=0;
while((1<<(k+1))<=(r-l+1))k++;
int a=d1[l][k],b=d1[r-(1<<k)+1][k];
if(cnt[a]<=cnt[b])x=a;
else x=b;
a=d2[l][k],b=d2[r-(1<<k)+1][k];
if(cnt[a]>=cnt[b])y=a;
else y=b;
}
int main()
{
int u,v;
while(scanf("%d",&N)!=EOF)
{
for(int i=0;i<=N;i++)G[i].clear();
for(int i=1;i<N;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs_clock=0;
dfs1(1,0,0);
preprocess();
scanf("%d",&Q);
while(Q--)
{
int l,r;
scanf("%d%d",&l,&r);
query(l,r,u,v);
printf("%d\n",LCA(u,v));
}
}
return 0;
}