【思维题】
【思维题】
打表多造数据很关键!!!!!!!
奇数 偶数
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
->类与类内部不能交换,类与类之间均可交换
结论
若众数<一半 排序后直接对半分
否则全部对齐众数
->若最大值超过一半->全部对齐交换
->否则均可交换->可能会剩一两个
代码
#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->即为对答案的贡献
代码
#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;
}