【思维题】

【思维题】

打表多造数据很关键!!!!!!!
奇数 偶数
2的幂次
倍数 质数
对称性质
头尾01

多观察规律
多想性质
多用代码行数短的做法

注意分类讨论!!!

Scarecrow

跳格子问题
直接考虑最简单的情况:一跳到底

https://codeforces.com/contest/2055/problem/D

思路

第一个点必须在0 
考虑两个点的情况
(1)若两个点距离小于k:不需要挪动可以直接跳
(2)若两个点距离大于k但小于k+当前挪动所花的时间:可以从一开始就挪到a[i-1]+k
(3)若两个点距离大于k+当前挪动所花的时间:a[i]从一开始就要挪动,并在到达a[i-1]时相向而行 

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int t; 
int n,k,l;
//记得开长整型! 
long double a[N];
long double ans,timenow; 
void solve(){
	cin>>n>>k>>l;
	for(int i=1;i<=n;i++) cin>>a[i];
	//初始化第一个点
	ans=a[1]*2;
	timenow=a[1];
	a[1]=0;
	for(int i=2;i<=n;i++){
		if(a[i]-a[i-1]>k){
			if(a[i]-a[i-1]>k+timenow){
				a[i]-=timenow;//在一开始就要挪
				ans+=(a[i]-a[i-1]-k);
				timenow+=(a[i]-a[i-1]-k)/2;//相向而行 所以时间/2 
				a[i]-=(a[i]-a[i-1]-k)/2;
			}
			else{
				a[i]=a[i-1]+k;
			}
		}
		else{
			a[i]=min(a[i-1]+k,a[i]+timenow);
		}
	}
	//最后一个点到终点
	ans+=max((l-a[n]-k)*2,(long double)0);
	cout<<(ll)ans<<"\n";
}
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>t;
      while(t--){
      	solve();
	}
      return 0;
}

翻之

https://ac.nowcoder.com/acm/contest/100671/C

代码

//把每一列存下来 一样的最多的就是答案(这些列能够同时变成1)
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=3010;
string a[N];
int n,m;
string s[N];
map<string,int> q;
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n>>m;
      for(int i=0;i<n;i++) cin>>s[i];
      for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                  a[j]+=s[i][j];
            }
      }
      for(int i=0;i<m;i++){
            q[a[i]]++;
      }
      int ans=-1;
      for(auto [key,value]:q){
            ans=max(ans,value);
      }
      cout<<ans;
      return 0;
}

mex和gcd的乘积

https://ac.nowcoder.com/acm/contest/66877/C

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=100010;
int n,a[N];
int t[N];
int gcd(int a,int b){
      if(a==0) return b;
      if(b==0) return a;
	return b?gcd(b,a%b):a;
}
/*【本题需要分类讨论!!!】
当一个区间的
mex>1:一定包含1 那么最大值就是mex
mex==1:数组只有0和>1的数 那么我们就选0和0前后最大的一个数
mex==0:答案为0
->注意是一题区间问题:
答案即为mex和gcdmax的较大值(mex>=1时)
注意要特判 整个数组为0时答案就是0!!!
*/
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n;
      for(int i=1;i<=n;i++){
            cin>>a[i];
            t[a[i]]++;
      }
      int mex=0;
      int ans=0;
      while(t[mex]) mex++;
      if(mex>=1){
            int gcdmax=mex;
            for(int i=1;i<=n;i++){
                  if(a[i]==0){
                        gcdmax=max(gcdmax,a[i-1]);
                        gcdmax=max(gcdmax,a[i+1]);
                  }
            }
            ans=gcdmax;
      }
      else if(mex==0) ans=0;
      //要特判整个数组为0时 答案为0
      if(t[0]==n) ans=0;
      cout<<ans;
      return 0;
}

Cost of the Array

https://codeforces.com/contest/2059/problem/B
考虑特殊情况下的特判
一般情况下的简单性

思路

当k==n时,答案唯一 -> 直接模拟
当k!=n时,答案只能从1和2中产生
->枚举每个第二个数组的首位2~n-k+2
如果全为1->答案为2
否则答案为1

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=2e5+10;
int t;
int n,k;
int a[N];
void solve(){
      cin>>n>>k;
      for(int i=1;i<=n;i++) cin>>a[i];
      int ans=1;
      if(n==k){
            vector<int> q;
            for(int i=2;i<=n;i+=2){
                  q.push_back(a[i]);
            }
            q.push_back(0);
            for(int j=0;j<q.size();j++){
                  if(q[j]!=j+1){
                        ans=j+1;
                        break;
                  }
            }
      }
      else{
            //枚举第二个被划分数组起点:范围从2~n-k+2:如果只有1那答案为2
            ans=2;
            for(int i=2;i<=n-k+2;i++){
                  if(a[i]!=1){
                        ans=1;
                        break;
                  }
            }
      }
      cout<<ans<<endl;
}
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>t;
      while(t--) solve();
      return 0;
}

Tokitsukaze and Balance String (hard)

打表找规律->就可以发现问题没那么复杂
当没思路的时候就从最简单的情况造样例考虑
https://ac.nowcoder.com/acm/contest/95336/C

/*
打表找规律!!!
过的人多->规律题!
*/
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const ll mod=1e9+7;
int t;
int n;
string s;
/*
不要管问号 打表找规律
首尾相同的一定平衡
首位不同的一定不平衡
->平衡的就只需要变中间的就可
->分类讨论
*/
ll qmi(ll a,ll k,ll p){
	ll res=1;
	while(k){
		if(k&1) res=res*a%p;
		k>>=1;//删去k的末位 
		a=a*a%p;
	}
	return res;
}
void solve(){
      cin>>n;
      cin>>s;
      ll ans=0;
      if(n==1){
            if(s[0]=='?') ans=2;
            else ans=1;
      }
      else{
            //统计?个数
            int cnt=0;
            for(int i=0;i<n;i++){
                  if(s[i]=='?') cnt++;
            }
            //首尾都是问号
            if(s[0]=='?' && s[n-1]=='?'){
                  //为了平衡:都为0或者都为1
                  ll res=qmi(2,cnt-2,mod);
                  /*首尾四种情况:
                  1 1/0 0->只能变中间n-2
                  1 0/0 1->只能变两边随便一个->2
                  ->(n-2+2)=n 有合并所以*2
                  */
                  res=res*n%mod;
                  res=res*2%mod;
                  ans=res;
            }
            //首尾有1个是问号
            else{
                  if(s[0]=='?'){
                        ll res=qmi(2,cnt-1,mod);
                        /*如果首和尾相同->变中间 +(n-2)
                        首和尾不同->变首或者变尾都行 +2
                        */
                        res=res*n%mod;
                        ans=res;
                  }
                  else if(s[n-1]=='?'){
                        ll res=qmi(2,cnt-1,mod);
                        res=res*n%mod;
                        ans=res;
                  }
                  //首尾都不是问号
                  else{
                        ll res=qmi(2,cnt,mod);
                        if(s[0]!=s[n-1]){//首尾不同
                              //首尾必须变一个
                              res=res*2%mod;
                        }
                        else{//首尾相同:只能变中间
                              res=res*(n-2)%mod;
                        }
                        ans=res;
                  }
            }
            
      }
      cout<<ans<<endl;
}
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>t;
      while(t--) solve();
      return 0;
}

Customer Service

https://codeforces.com/contest/2059/problem/C

思路

观察样例:发现答案和后缀1的个数有关系
后缀1的个数表示最大能提供的数(而应当删连续后缀1前面的那个)
(注意是最大!!!小值也可以
数据:
1
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
->答案是4而不是5
)
->求后缀1,排序,然后不断累计答案(只要当前个数>=ans ans就能累加)

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
const int INF=0x3f3f3f3f;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=310;
int t;
int n;
int a[N][N];
void solve(){
      cin>>n;
      for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j];
      vector<int> q;
      for(int i=1;i<=n;i++){
            int cnt=0;
            for(int j=n;j>=1;j--){
                  if(a[i][j]==1) cnt++;
                  else break;
            }
            q.push_back(cnt);
      }
      //找最大连续后缀1的个数
      sort(q.begin(),q.end());
      int ans=0;
      for(int i=0;i<n;i++){
            if(q[i]>=ans) ans++;
      }
      cout<<ans<<endl;
}
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>t;
      while(t--) solve();
      return 0;
}

小L的位运算

https://ac.nowcoder.com/acm/contest/95337/C
【首先想到分类讨论】
【其次想到贪心】

思路

显然,已经满足条件的位置不需要再变更
->不满足条件的位置分4类:01 10 11 01
->类与类内部不能交换,类与类之间均可交换

结论

若众数<一半 排序后直接对半分
否则全部对齐众数

image

->若最大值超过一半->全部对齐交换
->否则均可交换->可能会剩一两个

image

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
int n;
ll x,y;
string a,b,c;
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n>>x>>y;
      cin>>a>>b>>c;
      /*
      【思路】
      统计答案和c对不上的 00 01 10 11个数
      每个组之间都能对换->优先换大的,直到剩一个数
      最后剩下的就只能反置了
      */
      ll zz=0,zo=0,oz=0,oo=0;
      for(int i=0;i<n;i++){
            int ai=a[i]-'0';
            int bi=b[i]-'0';
            int ci=c[i]-'0';
            if(ai^bi!=ci){
                  if(ai==0 && bi==0) zz++;
                  else if(ai==1 && bi==0) oz++;
                  else if(ai==0 && bi==1) zo++;
                  else if(ai==1 && bi==1) oo++;
            }
      }
      ll ans=0;
      /*
      (1)如果最大的超过了一半 那么全部盯着最大的看(所有类型都不能内部消化 和最大的消化最好) 最大减完然后反置
      (2)如果最大的没超过一半->内部会全部消化(结论) 最后剩下0或1个用来反置
      */
      vector<ll> q;
      ll sum=zz+oo+zo+oz;
      q.push_back(zz);
      q.push_back(oo);
      q.push_back(zo);
      q.push_back(oz);
      sort(q.begin(),q.end());
      ll mx=q[3];
      if(mx>sum/2){
            ans=(sum-mx)*y+(mx-(sum-mx))*x;
      }
      else{
            ans=(sum/2)*y+(sum&1)*x;
      }
      if(x*2<y){
            ans=x*sum;
      }
      cout<<ans;
      return 0;
}

小紫的优势博弈

思路

※※※从后往前构建
在%2环境下 有4种状态
ze on
0  0->直接算结果
1  0
0  1
1  1
->发现后三种情况只会不能满足条件1次:奇数个0/奇数个1/1个0一个1
->判断情况 遇到解锁

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
int n;
string s;
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n;
      cin>>s;
      s=' '+s;
      vector<int> ze(n+2,0),on(n+2,0);
      for(int i=n;i>=1;i--){
            if(s[i]=='0'){
                  ze[i]=ze[i+1]+1;
                  on[i]=on[i+1];
            }
            else if(s[i]=='1'){
                  ze[i]=ze[i+1];
                  on[i]=on[i+1]+1;
            }
      }
      int res=0;
      int tag0=1,tag1=1,tag01=1;
      for(int i=n;i>1;i--){
            if(ze[i]%2==0 && on[i]%2==0) res++;
            else if(ze[i]%2==1 && on[i]%2==0){
                  if(tag0) tag0=0;
                  else res++;
            }
            else if(ze[i]%2==0 && on[i]%2==1){
                  if(tag1) tag1=0;
                  else res++;
            }
            else if(ze[i]%2==1 && on[i]%2==1){
                  if(tag01) tag01=0;
                  else res++;
            }
      }
      if(n<=2) res=0;
      double ans=(double)res/(double)n;
      cout<<fixed<<setprecision(7)<<ans<<endl;
      return 0;
}

mex

https://ac.nowcoder.com/acm/contest/105825/D

思路

首先考虑无解/不用操作的情况:
(1)如果整个数组没有0->mex为0->怎么减都没办法减到相同->无解
(2)如果整个数组本来就是全相同->不需要操作->解为0
然后考虑扣mex的情况:
首先给数组排序
因为每个大的数都会依托于小的数扣
比如 0 1 2 5->5会被0 1 2产生的mex扣:扣去3
所以产生差分公式:a[i]-a[i-1]-1 记得mex比该数多1->再多减1->即为对答案的贡献

image

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a<b;}
const int N=1e5+10;
int n;
ll a[N];
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n;
      int is_same=0;
      bool is_zero=0;
      for(int i=1;i<=n;i++){
            cin>>a[i];
            if(a[i]==0) is_zero=1;
            if(a[i]==a[1]) is_same++;
      }
      if(is_same==n) cout<<0;
      else if(!is_zero) cout<<-1;
      else{
            sort(a+1,a+1+n,cmpll);
            ll ans=1;
            for(int i=2;i<=n;i++) ans+=max_(0,a[i]-a[i-1]-1);//注意这里 如果对答案没贡献(比如两个数相同)那么贡献为0->注意区最大值
            cout<<ans;
      }
      return 0;
}

小红开灯(三,hard)

https://ac.nowcoder.com/acm/contest/107000/D
一定要注意到本题k比n的关系大!!!

思路

(1)先考虑n和k的关系问题
①若k==n:只有全0和全1两种状态 -> 特判
②若k<n改变k盏灯->改变任意2盏灯的状态
(2)打表 考虑结论
k是奇数结合上面结论->我可以改变任何一盏灯的状态->2^n
k是偶数:我只能改变偶数盏灯的状态
C(n,0)+C(n,2)+C(n,4)+...+C(n,2k)->2^(n-1)

代码

const ll mod=1e9+7;
ll n,k;
ll qmi(ll a,ll k,ll p){
    ll res=1;
    while(k){
        if(k&1) res=res*a%p;
        k>>=1;
        a=a*a%p;
    }
    return res;
}
void solve(){
    cin>>n>>k;
    if(n==k) cout<<"2"<<endl;
    else if(k<n){
        if(k%2){
            ll ans=qmi(2,n,mod);
            cout<<ans<<endl;
        }
        else{
            ll ans=qmi(2,n-1,mod);
            cout<<ans<<endl;
        }
    }
}

Asuna and the Mosquitoes

https://codeforces.com/contest/2092/problem/C
别想太多!性质很简单 奇数化成1

int n;
/*
【结论题】
1.若只有奇数/偶数:最大
2.若同时拥有:答案为和-奇数个数+1
->选择一个偶数 构造偶数+(奇数-1)=偶数 奇数->1
->偶数可以完全吃掉一个奇数
->有多个偶数?最后只能留一个偶数吃掉变成奇数 其他最后都会变成奇数->化成1
*/
void solve(){
    cin>>n;
    vector<ll> a(n+1,0);
    ll sum=0;
    ll maxx=0,jicnt=0,oucnt=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
        maxx=max_(a[i],maxx);
        if(a[i]%2) jicnt++;
        else oucnt++;
    }
    if(jicnt==0 || oucnt==0){
        cout<<maxx<<endl;
    }
    else{
        cout<<(sum-jicnt+1LL)<<endl;
    }
}

操作字符串

https://ac.nowcoder.com/acm/contest/107879/D
注意“最小公倍字符串”和最大公约数的关系
字母只有26个 考虑O(26*N)

int b[N][27];
void solve(){
    cin>>n;
    int len=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        int l=a[i].size();
        if(i==1) len=l;
        else len=gcd(len,l);
    }
    //cout<<len<<endl;
    for(int i=1;i<=n;i++){
        int l=a[i].size();
        for(int j=0;j<l;j++){
            int tt=a[i][j]-'a';
            b[j%len][tt]++;
        }
    }
    for(int i=0;i<len;i++){
        sort(b[i],b[i]+26,cmp);
        /*
        for(int j=0;j<26;j++){
            cout<<b[i][j]<<" ";
        }
        cout<<endl;
        */
    }
    int ans=0;
    for(int i=0;i<len;i++){
        for(int j=1;j<26;j++){
            ans+=b[i][j];
        }
    }
    cout<<ans<<endl;
}

https://ac.nowcoder.com/acm/contest/109904/C
先想到一些显然的结论 然后写下来造数据->能找到规律

int n;
int a1,b1,a2,b2;
/*
【结论题分类讨论】
后面必须追上 前面必须被阻挡:已知每次阻挡+1格 只有在前面的可以阻挡:只能被挡1次
(1)两点相同位置
(2)A 0 : 可以在B的路线上挡一格 使得B多加1格
   0 B
(3)0 B : B本来就需要下来1格
   A 0
※注意B不能是终点或者终点前1列
->总结
(1)两点相同一定YES
(2)两点不同:①在前面的点只能在后面的点1格 
            ②在前面的点不能在n和n-1列?
              0 B
              A 0
              这种可以:因为不需要障碍
              A 0
              0 B
              这种不可以:因为需要障碍
*/
void solve(){
    //A永远在上面 B永远在下面
    cin>>n;
    cin>>a1>>b1>>a2>>b2;
    if(a1==a2 && b1==b2){
        cout<<"YES"<<endl;
        return;
    }
    if(b1>b2){//小的点在前面 大的点在后面
        swap(a1,a2);
        swap(b1,b2);
    }
    if(b1+1==b2){//在前面的点只能在后面的点前1格
        if(a1==a2+1){
           cout<<"YES"<<endl;
           return;
        }
        if(a1+1==a2 && b2<n-1){//在前面的点不能是n和n-1 a的+1-1关系随便
            cout<<"YES"<<endl;
            return;
        }
    }
    cout<<"NO"<<endl;
}
posted @ 2025-01-19 18:51  White_ink  阅读(12)  评论(0)    收藏  举报