目录
Match or Not
题意:给定两个字符串,问,对于s字符串的x个前缀和len(t)-x个后缀组成的新字符串s‘是否和字符串t相同。
思路:我们可以去检索所以不符合要求的下标,用set进行维护,如果set为空,则该字符串合法,否则输出no,再者,因为x是递增的,所以我们只检索对应的下标即可,不用全部重新遍历,(因为如果当前位置不合法(不匹配),接下来的下标,无论是否合法,对于整个字符串而言,都是不匹配的)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<int,int > pll;
int mod=1e9+7;
void solve()
{
string s,t;
cin>>s>>t;
int n=s.size(),m=t.size();
set<int> z;
int j=0;
for(int i=n-m;i<n;i++,j++){
if(s[i]!='?'&&t[j]!='?'&&s[i]!=t[j]){
z.insert(j);
}
}
if(z.empty()){
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
for(int i=0;i<m;i++){
z.erase(i);
if(s[i]!='?'&&t[i]!='?'&&s[i]!=t[i]){
z.insert(j);
}
if(z.empty()){
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
//system("pause");
return 0;
}
Divide by 2 or 3
题意:给定指定长度的数组,对于数组中的数字,如果能被2或3整除,可以将该数除以2或3(当然也可以不操作)问,经过有限次操作后,是否能使数组中所有数字相同。
思路:一开始的想法是,寻找最小值,然后看每个数是否能通过除以2或3变成最小值,但后来发现,存在一种情况,即有的数不能变到最小值,但最后整个数组依然合法。(比如3和16,我们可以通过操作将其全部变为1 1)。所以接下来就考虑求出整个数组的gcd,将上文所述的最小值替换为gcd即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long ll;
typedef pair<int,int> pll;
int mod=1e9+7;
void solve()
{
int n;
cin>>n;
vector<int> a(n);
ll cnt=0;
int gcd=0;
for(int i=0;i<n;i++){
cin>>a[i];
gcd=__gcd(gcd,a[i]);
}
for(int i=0;i<n;i++){
if(a[i]%gcd!=0){
cout<<-1<<endl;
return ;
}
else{
int s=0;
int x=a[i]/gcd;
while(x%3==0){
x/=3;
s++;
}
while(x%2==0){
x/=2;
s++;
}
if(x>1){
cout<<-1<<endl;
return;
}
else{
cnt+=s;
}
}
}
cout<<cnt<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
while(t--){
solve();
}
return 0;
}
求和
题意:由于是中文,就不再赘述了。
思路:对题目进行浏览后,题目要我们所求的式子,我们可以发现,跟题目所给出的y没有关系。那么,对第一个条件进行翻译,即为,选择两个位置x,z,让y为x,z的平均数且为整数,即再通俗易懂一点就是,保证x+z的值一定是偶数。
再进行第二个条件的翻译,保证x和z方块上的颜色相等。
题目给出的number数组其实在整个题目中,只在最后求答案的时候起了作用,所以不要被题目花里胡哨的数据给吓到。别看有下标,颜色,编号,看起来很复杂,其实只是考察的推式子(应该存在其他做法,若觉得笔者复杂可以自行搜素)
如上文所说,我们需要保证x和z的颜色相同,且x+z的和为偶数,那么我们就可以将相同颜色的下标用map进行维护,然后去对每个颜色所存的下标进行遍历,最后进行累加。但对于n个相同的颜色,无论是否合法(即相加为偶数)的话,遍历的 的时间复杂度为n*n,对于此题的数据会tle。所以我们要想方法进行优化,因为要去检查是否合法,我们就想,是否可以将下标进行分类,然后运用一些式子,把答案计算出来。我们可以发现,下标非奇即偶,因此我们可以把下标的为奇数的存一个数组,偶数的存一个数组,这样就解决了对于合法的检验。那么剩下来的就只剩如何快速求出答案。我们可以尝试一下,比如,偶数数组中有三个数,那么偶数数组的答案总和为:
我们对其进行化简,可以得到(2*a1*num1+a1*num2+a1*num3)+(2*a2*num2+a2*num1+a2*num3)+(2*a3*num3+a3*num1+a3*num2),结构相同,我们对于每一个下标,单独拿出看,为a1*num1+a1*(num1+num2+num3)。
这里直接给出结论,对于长度为n的公式,单独拆开来看,其组成为:
由此我们可以预处理出前n个num的和,然后就可以成功将n*n的时间复杂度降为n的时间复杂度。
最后注意取模就好。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long ll;
typedef pair<int,int> pll;
int mod=10000+7;
ll num[N],co[N];
void solve()
{
int n,m;
cin>>n>>m;
ll ans=0;
map<ll,vector<ll> > mp;
for(int i=1;i<=n;i++){
cin>>num[i];
}
for(int i=1;i<=n;i++){
cin>>co[i];
mp[co[i]].push_back(i);
}
for(auto x: mp){
auto v=x.second;
vector<int> a,b;
ll sum1=0,sum2=0;
for(int i=0;i<v.size();i++){
if(v[i]%2==0){
a.push_back(v[i]);
sum1+=num[v[i]];
}
else{
b.push_back(v[i]);
sum2+=num[v[i]];
}
}
for(auto x: a){
ans=(ans%mod+((a.size()-2)*x*num[x]+x*sum1)%mod)%mod;
}
for(auto x: b){
ans=(ans%mod+((b.size()-2)*x*num[x]+x*sum2)%mod)%mod;
}
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
//system("pause");
return 0;
}
[蓝桥杯 2018 国 B] 调手表
思路:BFS的应用。我们可以将起点看作0,然后把终点看作1-n中的所有数,最后所求即为到打个点最小值的最大值。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<int,int > pll;
int mod=1e9+7;
bool st[N];
int t[N];
void solve()
{
int n,k;
cin>>n>>k;
queue<int> q;
q.push(0);
st[0]=1;
int ans=0;
while(!q.empty()){
int fr=q.front();
//cout<<t[fr]<<endl;
q.pop();
ans=max(ans,t[fr]);
int x=(fr+k)%n,y=(fr+1)%n;
if(!st[x]){
t[x]=t[fr]+1;
st[x]=1;
q.push(x);
}
if(!st[y]){
t[y]=t[fr]+1;
st[y]=1;
q.push(y);
}
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
[NOIP2008 普及组] 排座椅
思路:开两个结构体,去维护行和列的信息,一开始所有的价值为0,对于题目给定的两个坐标,若横坐标不同,即为行不同,min(x1,x2)所对应的行的价值++,对于列同理。最后对价值进行排序,对于行和列输出对应的个数即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<int,int > pll;
int mod=1e9+7;
struct node
{
int v,l,r;
}h[N],li[N];
void solve()
{
int m,n,k,l,d;
cin>>m>>n>>k>>l>>d;
for(int i=1;i<m;i++){
h[i].l=i;
h[i].r=i+1;
}
for(int i=1;i<n;i++){
li[i].l=i;
li[i].r=i+1;
}
for(int i=0;i<d;i++){
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
if(x1!=x2){
h[min(x1,x2)].v++;
}
else{
li[min(y1,y2)].v++;
}
}
sort(h,h+m,[](node a,node b){return a.v>b.v;});
sort(h,h+k,[](node a,node b){return a.l<b.l;});
sort(li,li+n,[](node a,node b){return a.v>b.v;});
sort(li,li+l,[](node a,node b){return a.l<b.l;});
for(int i=0;i<k;i++){
cout<<h[i].l<<" ";
}
cout<<endl;
for(int i=0;i<l;i++){
cout<<li[i].l<<" ";
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
//system("pause");
return 0;
}
[IOI2000] 回文字串
思路:将字符串翻转然后对于两个字符串,求出LCS,用n减去LCS的值即可。
#include<bits/stdc++.h>
using namespace std;
int dp[1005][1005];
int main()
{
string a;
cin>>a;
int n=a.size();
string b=a;
reverse(a.begin(),a.end());
a=' '+a;
b=' '+b;
for(int i=1;i<a.size();i++){
for(int j=1;j<b.size();j++){
if(a[i]!=b[j]){
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
else{
dp[i][j]=dp[i-1][j-1]+1;
}
}
}
cout<<n-dp[n][n]<<endl;
system("pause");
return 0;
}
口袋的天空
思路:
有一句话说的是 如果n个点被n-1条边连接的话,这一定是棵树。
那么:
连的边数 得到的树的个数
n-1 1
n-2 2
n-3 3
... ...
n-k k
所以我们如果想要连出k棵树,就需要连n-k条边。题目要求用n朵云连出k个棉花糖。因为每个棉花糖都是连通的,那么每个棉花糖就相当于是一棵树。就是说要用n个节点连出k棵树。
也就是说要用n-k条边连出k棵树。也就是说要花费连出n-k条边的代价。
简言之就是,构造出k个节点的最小生成树。
//kruskal求最小生成树,适用于稀疏图,时间复杂度mlogm
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
struct node
{
int a,b,c;
}e[N];
int n,m,k;
int p[N];
int find(int x)
{
if(p[x]!=x){
return p[x]=find(p[x]);
}
return x;
}
void krustal()
{
int res=0;
int cnt=n;
for(int i=0;i<m;i++){
if(cnt==k){
cout<<res<<endl;
return ;
}
int a=e[i].a,b=e[i].b,c=e[i].c;
int pa=find(a),pb=find(b);
if(pa!=pb){
res+=c;
cnt--;
p[pa]=pb;
}
}
if(cnt!=k){
cout<<"No answer"<<endl;
}
else{
cout<<res<<endl;
}
}
bool cmp(node x,node y)
{
return x.c<y.c;
}
int main()
{
cin>>n>>m>>k;
int i;
for(i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c;
e[i]={a,b,c};
}
for(i=0;i<=n;i++){
p[i]=i;
}
sort(e,e+m,cmp);
krustal();
system("pause");
return 0;
}
尴尬的数字
题意: 给定一个数的二进制和三进制表示,二进制表示和三进制表示中,有一位是错误的,要你输出该的正确值。
思路:题目所说,n可能到1e9,就算到1e18都没有关系,因为二进制表示只有那么多位数,所有我们对每一位二进制和三进制表示数的每一位进行修改,然后记录值,用map进行维护,最后value值为2的即为答案。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
typedef pair<int,int > pll;
int mod=1e9+7;
ll get2(vector<int> x)
{
int n=x.size()-1;
ll res=0;
for(int i=0;i<x.size();i++){
res+=x[i]*pow(2,n);
n--;
}
return res;
}
ll get3(vector<int> x)
{
int n=x.size()-1;
ll res=0;
for(int i=0;i<x.size();i++){
res+=x[i]*pow(3,n);
n--;
}
return res;
}
void solve()
{
string a,b;
cin>>a>>b;
vector<int> n2,n3;
for(int i=0;i<a.size();i++){
n2.push_back(a[i]-'0');
}
for(int i=0;i<b.size();i++){
n3.push_back(b[i]-'0');
}
map<ll,int> z;
for(int i=0;i<n2.size();i++){
ll res=0;
if(n2[i]==0){
n2[i]=1;
res=get2(n2);
n2[i]=0;
}
else{
n2[i]=0;
res=get2(n2);
n2[i]=1;
}
z[res]++;
}
for(int i=0;i<n3.size();i++){
ll res=0;
if(n3[i]==0){
n3[i]=1;
res=get3(n3);
z[res]++;
n3[i]=2;
res=get3(n3);
z[res]++;
n3[i]=0;
}
else if(n3[i]==1){
n3[i]=0;
z[get3(n3)]++;
n3[i]=2;
z[get3(n3)]++;
n3[i]=1;
}
else{
n3[i]=1;
z[get3(n3)]++;
n3[i]=0;
z[get3(n3)]++;
n3[i]=2;
}
}
for(auto x: z){
if(x.second==2){
cout<<x.first<<endl;
return ;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
//system("pause");
return 0;
}