A:ABB
题目大意:给一个固定长度的字符串,问要在右边最少加几个字符能使它整体形成回文串。
思路:要在末尾加最短的字符形成回文串,其实也就是找最靠左的中心点,我们也知道中心点起码都在原来的串上,因为加一倍的长度是必定能构成回文串。所以我们只要找到能做中心点的且是最靠左的下标。那此时就运用
Manacher算法,算法中有一个数组p[i]是可以算出一个串中以i点为中心能构成的最长回文子串的半径(包含中心点)的,那么再回到本题中,显然,如果这个下标 i 加上它的(p[i]-1)还没到原字符串末尾的话,那么必然不能构成回文串,因为这种情况字符串自身的某些元素都无法构成回文,加元素也没用,也就是这个点无法作为中心点。那如果下标i加上(p[i]-1)刚好就等于字符串末尾,那么就好办了,只要把(i-(p[i]-1))前面的元素逆序加到末尾就可以了,也就是说这种情况是可以满足加元素形成回文串的。那其实我们也可以发现,对于任意一个下标,只有这两种情况,加上(p[i]-1)小于末尾下标,和加上(p[i]-1)等于末尾下标。那么这个时候问题就好解决了,直接从左边往后遍历,找到第一个加上(p[i]-1)等于末尾下标的元素,也就是从左往右数第一个能做中心点的下标,即最靠左的中心点。最终的答案还需根据Manache算法做一个判断然后计算。
#include<bits/stdc++.h>
using namespace std;
int Manacher(string str) {
if (str.length() == 0) {
return 0;
}
int len = (int)(str.length() * 2 + 1);
char *chaArr = new char[len];
int* pArr = new int[len];
int index = 0;
for (int i = 0; i < len;i++) {
chaArr[i] = (i & 1) == 0 ? '#' : str[index++];
}
int R = -1;
int C = -1;
int maxn = 0;
for (int i = 0; i < len; i++) {
pArr[i] = R > i ? min(R - i, pArr[2 * C - i]) : 1;
while (i + pArr[i]<len && i - pArr[i]>-1) {
if (chaArr[i + pArr[i]] == chaArr[i - pArr[i]]) {
pArr[i]++;
}
else {
break;
}
}
if (i + pArr[i] > R) {
R = i + pArr[i];
C = i;
}
maxn = max(maxn, pArr[i]);
}
for(int i=0;i<len;++i)
{
if(pArr[i]+i-1==len-1)
{
if(chaArr[i-pArr[i]+1]=='#')
cout<<(i-pArr[i]+1)/2;
else
cout<<(i-pArr[i])/2;
break;
}
}
delete[] chaArr;
delete[] pArr;
}
int main()
{
int n;
cin>>n;
string x;
cin>>x;
Manacher(x);
}
C. Bob in Wonderland
题目大意:给n个点,n-1条边所构成的一个树(一定是连同无环图(树),因为n个点只有n-1条边,且题意中说明了所有节点在一个图中),然后通过拆边再重新接上的方式(拆开开一条边之后,其它的节点的其他边不受影响),算出最少多少步操作可以使得其变为一个直链。
思路:起初写这题的时候有点迷惑了,一眼望去跟图论一样。。。想着人家最小生成树是含环图变树,这题是树变直链,还想着有什么联系。结果看着这题贼多人写出来了,就想着可能真的是我想太多。结果确实,这题硬要说贪心也行,但我个人感觉有点像思维题,只不过这个思维比较简单。对于一个分岔路而言,一步操作最多最多每次也只能使得这个图减少一条岔路,而且是一定可以做到减少一条岔路。那这题就解决了,算下岔路个数就完事了。两种算岔路个数的方法:(1)1度节点个数-2(2)所有度大于2的节点的度数总和-2*度大于2的节点的总个数(就是每条岔路留下要走的那两条路之后还剩下多少条路)。显然第一个更简单算一些。
注意处理n=1这个特殊情况
#include<bits/stdc++.h>
using namespace std;
vector<int> G[3*100010];
int main()
{
int n,s,e;
cin>>n;
if(n==1)
{
cout<<0;
return 0;
}
for(int i=0;i<n-1;++i)
{
scanf("%d%d",&s,&e);
G[s].push_back(e);
G[e].push_back(s);
}
int con=0;
for(int i=1;i<=n;++i)
{
if(G[i].size()==1)
con++;
}
cout<<con-2;
}
E. Zeldain Garden
#include <bits/stdc++.h>
using namespace std;
#define PB push_back
#define ZERO (1e-10)
#define INF int(1e9+1)
#define CL(A,I) (memset(A,I,sizeof(A)))
#define DEB printf("DEB!\n");
#define D(X) cout<<" "<<#X": "<<X<<endl;
#define EQ(A,B) (A+ZERO>B&&A-ZERO<B)
typedef long long ll;
typedef pair<ll,ll> pll;
typedef vector<int> vi;
typedef pair<int,int> ii;
typedef vector<ii> vii;
#define IN(n) int n;scanf("%d",&n);
#define FOR(i, m, n) for (int i(m); i < n; i++)
#define F(n) FOR(i,0,n)
#define FF(n) FOR(j,0,n)
#define FT(m, n) FOR(k, m, n)
#define aa first
#define bb second
void ga(int N,int *A){F(N)scanf("%d",A+i);}
ll X,N,Q,S;
ll go(ll N){
if(!N)return 0;
Q=sqrt(N+ZERO),S=0;
for(ll i=1;i<=Q;++i)S+=N/i;
return ll(S*__int128(2)-__int128(Q)*Q);
}
int main(void){
scanf("%lld%lld",&X,&N);
printf("%lld\n",go(N)-go(X-1));
return 0;
}
F. Light Emitting Hindenburg
题目大意:从n个数中取k个数出来,求k个数进行与运算后的最大值。
思路:典型贪心。算与运算后的最大值,那么就是尽量高位为1就行。那就直接从高位往低位遍历,对于第 i 位,去判断现存的数中有多少个第 i 位为1的,因为既然要使得与运算后第 i 位为1,而且只能取k个数字运算,那么也就是在现存的数字里面第 i 位是1的数字的数量要大于等于k,这样就能使得第 i 位是1,不然这一位只能是0。那么只要判断成功,我们就要把这第 i 位是1的数字赋值为现存数字,也就是答案就在这些数字里面,然后继续一位一位往下走,直到最后一位走完。那么最后让现存的数字(不一定是k个数,可能有多种组合情况)全部做与运算就是最终答案了。
#include<bits/stdc++.h>
using namespace std;
vector<int> a,b;
int main()
{
int n,k,num;
cin>>n>>k;
for(int i=0;i<n;++i)
{
scanf("%d",&num);
a.push_back(num);
}
for(int i=1<<29;i>0;i>>=1)
{
b.clear();
for(int j=0;j<a.size();++j)
{
if(a[j]&i)
b.push_back(a[j]);
}
if(b.size()>=k)
a=b;
}
int ans=~0;
for(int i=0;i<a.size();++i)
ans&=a[i];
cout<<ans;
}
或者(也没啥大区别,只是可以少写个for循环)
#include<bits/stdc++.h>
using namespace std;
vector<int> a,b;
int main()
{
int n,k,num;
cin>>n>>k;
for(int i=0; i<n; ++i)
{
scanf("%d",&num);
a.push_back(num);
}
int ans=0;
for(int i=1<<29; i>0; i>>=1)
{
b.clear();
for(int j=0; j<a.size(); ++j)
{
if(a[j]&i)
b.push_back(a[j]);
}
if(b.size()>=k)
{
ans+=i;
a=b;
}
}
cout<<ans;
}