题1:https://codeforces.com/contest/1768/problem/D
题目1:https://loj.ac/p/10238
题2:https://ac.nowcoder.com/acm/contest/41173/B
将字符串按特定的顺序进行排序,我们可以将其用map映射,然后斤进行排序
题目链接:[Problem - E - Codeforces](https://codeforces.com/contest/1772/problem/E) 不错的思维题
题目链接:[Problem - D - Codeforces](https://codeforces.com/contest/1774/problem/D)
#include<bits/stdc++.h>
using namespace std;
const int N=100009;
int s[N];
struct node
{
int x,y,z;
};
int main()
{
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
int sum=0;
int g[n+2][m+2];
for(int i=1;i<=n;i++)
{
s[i]=0;
for(int j=1;j<=m;j++)
{
scanf("%d",&g[i][j]);
s[i]+=g[i][j];
}
sum+=s[i];
}
vector<node>res;
if(sum%n!=0)
{
cout<<-1<<endl;
continue;
}
int cnt=sum/n;
vector<int>a,b;
for(int j=1;j<=m;j++)
{
for(int i=1;i<=n;i++)
{
if(s[i]>cnt&&g[i][j]==1)a.push_back(i);
if(s[i]<cnt&&g[i][j]==0)b.push_back(i);
}
for(int k=0;k<min(a.size(),b.size());k++)
{
s[a[k]]--;
s[b[k]]++;
res.push_back({b[k],a[k],j});
}
a.clear(),b.clear();
}
cout<<res.size()<<endl;
for(auto i:res)cout<<i.x<<" "<<i.y<<" "<<i.z<<endl;
}
return 0;
}
题目链接:[Problem - D - Codeforces](https://codeforces.com/contest/1772/problem/D) 不错的二分题
#include<bits/stdc++.h>
using namespace std;
int a[200009];
int n;
int check(int x)
{
for(int i=1;i<n;i++)
{
if(fabs(a[i]-x)<=fabs(a[i+1]-x))continue;
else
{
if(x>=a[i]&&x>=a[i+1])return 1;
if(x<=a[i]&&x<=a[i+1])return 2;
if(a[i+1]>a[i])return 3;
return 4;
}
}
cout<<x<<endl;
return -1;
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int l=1,r=1e9;
while(l<=r)//记得加等于号
{
int mid=(l+r)/2;
cout<<l<<" "<<mid<<" "<<r<<endl;
int k=check(mid);
if(k==-1)goto skip;
if(k==1||k==3)r=mid-1;
else l=mid+1;
cout<<r<<endl;
}
cout<<-1<<endl;
skip:;
}
return 0;
}
题目链接:https://ac.nowcoder.com/acm/contest/49030/E
#include<bits/stdc++.h>
using namespace std;
long long n,mod=998244353,ans=1,x;
long long abc()//最小费马求逆元
{
long long a=12,res=1, b=mod-2;
while(b)
{
if(b&1) res=res*a%mod;
b>>=1;
a=(a*a)%mod;
}
return res%mod;
}
int main()
{
cin>>n;
if(n==1)
{
cout<<1<<endl;
return 0;
}
for(int i=1;i<=n+2;i++){ans*=i;ans%=mod;}
x=abc();
ans*=x;
ans%=mod;
cout<<ans<<endl;
return 0;
}
~~~
其实完全没必要求逆元
~~~c++
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 1, p = 998244353;
int n;
int main(){
scanf("%d", &n);
if(n == 1){
putchar('1');
return 0;
}
int ans = 2;
for (int i = 3; i <= n; i++)
ans = 1ll * ans * (i + 2) % p;
printf("%d", ans);
return 0;
}
题目链接:[Problem - 1744D - Codeforces](https://codeforces.com/problemset/problem/1744/D)
题意:给你两个字符串,起初都是”a“,然后给你一个k和一个字符串s告诉你给s1或s2尾部加k次,每加完一次,要你判断是否可以将两个字符串重新排序让s1的字典序小于s2。对于s1和s2随便你怎么排都可以。
#include<bits/stdc++.h>
using namespace std;
long long num1[30],num2[30];
long long sum1=0,sum2=0;
//我们只需
void solve(long long a,long long b)
{
for(int i=0;i<26;i++)
{
a-=num1[i];
b-=num2[i];
if(b>0)//如果字符串s2后面还有其字母,那么我们就可以将后面的字符放到前面来,这样s1的字典序自然就小于s2了。
{
cout<<"YES"<<endl;
return;
}
else if(num1[i]<num2[i]&&a==0)//如果s1当前的字符数小于s2并且后面没字符了,那自然s1的字典序就小于s2了。
{
cout<<"YES"<<endl;
return;
}
else//其他情况都是不行的
{
cout<<"NO"<<endl;
return;
}
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
sum1=1,sum2=1;
memset(num1,0,sizeof(num1));
memset(num2,0,sizeof(num2));
int q;
cin>>q;
num1[0]++;
num2[0]++;
for(int i=1;i<=q;i++)
{
long long d=0,k=0;
string s;
cin>>d>>k;
cin>>s;
if(d==1)
{
sum1+=((long long)s.size()*k);
for(int j=0;j<s.size();j++)
{
int cnt=s[j]-'a';
num1[cnt]+=k;
}
}
else
{
sum2+=((long long)s.size()*k);
for(int j=0;j<s.size();j++)
{
int cnt=s[j]-'a';
num2[cnt]+=k;
}
}
solve(sum1,sum2);
}
}
return 0;
}
思路:我们可以将每次加完的字符串的字符数都统计出来,然后进行比较就行了。
题目链接:[D - Divide by 2 or 3 (atcoder.jp)](https://atcoder.jp/contests/abc276/tasks/abc276_d#)
题意:给你一个数组,对于数组中的每个数,你可以对a[i]%2==0进行除2的操作,也可以对a[i]%3==0进行除3的操作。要求你求出将数组所有元素变成相同所需最小步数或者转换不了就输出0。
#include<bits/stdc++.h>
using namespace std;
int a[3000];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int h=__gcd(a[1],a[2]);
for(int i=2;i<=n;i++)
{
h=__gcd(h,a[i]);
}//求出组大公约数
int ans=0;
if(h==1)//最大公约数为1的情况
{
for(int i=1;i<=n;i++)
{
if(a[i]==h)continue;
if(a[i]%2!=0&&a[i]%3!=0)// .如果这个过程中我们碰到了不能模3或者2等于0的那么我们就无法通过除3或者除2转换成最大公约数
{
cout<<-1<<endl;
return 0;
}
int cnt=a[i]/h;//我们只需将cnt转换成1就行了,h为最大公约数
while(cnt!=1)
{
if(cnt%2!=0&&cnt%3!=0)
{
cout<<-1<<endl;
return 0;
}
while(cnt%3==0)
{
cnt/=3;
ans++;
}
while(cnt%2==0)
{
cnt/=2;
ans++;
}
}
}
}
else
{
for(int i=1;i<=n;i++)
{
if(h==a[i])continue;
if(a[i]%2!=0&&a[i]%3!=0)// .如果这个过程中我们碰到了不能模3或者2等于0的那么我们就无法通过除3或者除2转换成最大公约数
{
cout<<-1<<endl;
return 0;
}
int cnt=a[i]/h;//我们只需将cnt转换成1就行了,h为最大公约数
while(cnt!=1)
{
if(cnt%2!=0&&cnt%3!=0)
{
cout<<-1<<endl;
return 0;
}
while(cnt%3==0)
{
cnt/=3;
ans++;
}
while(cnt%2==0)
{
cnt/=2;
ans++;
}
}
}
}
cout<<ans<<endl;
return 0;
}
思路:我们将整个数组的最大公约数求出来,然后每个数转换成最大公约数的步数求出再加起来就行了,如果我们再求将a[i]转换成最大公约数的时候他不能a[i]%2==0||a[i]%3==0那么这个数我们就无法通过除2或者除3转换成最大公约数。如果a[i]可以通过除2除3转换成他们的最大公约数那么他转换的步数是确定的因为 a[i]=最大公约数*2(需要的) *3(需要的); 所以我们求a[i]转换成最大公约数就可以将a[i]可以除3就除3 能除2就除2 .如果这个过程中我们碰到了不能模3或者2等于0的那么我们就无法通过除3或者除2转换成最大公约数,此时我们还必须除另外的数才能将其转换成最大公约数。
题目链接:https://ac.nowcoder.com/acm/contest/44887/E
提提:给你n个集合,每个集合有一个或者多个元素,每个元素最多连一条边,要求你将所有的集合连接成一个集合,问你连接边数量最大的那个集合边数最小为多少。
#include<bits/stdc++.h>
using namespace std;
int n,m,cnt=0;
int f[200009],sz[200009];
int fi(int x)//并查集将为个元素都存为其集合根的值,节约查询的时间
{
if(x==f[x])return x;
f[x]=fi(f[x]);
return f[x];
}
bool check(int x)//x为一个集合连接多少边
{
int sum=0;
for(int i=1;i<=n;i++)sum+=min(x,sz[i]);
if(sum>=(cnt-1)*2)return true;//cnt为集合的个数
else return false;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
f[fi(x)]=fi(y);//将x集合的根,更新为y集合的根
}
for(int i=1;i<=n;i++)sz[fi(i)]++;//统计每个集合的个数,f[i]找到该元素位于那个集合
for(int i=1;i<=n;i++)if(sz[i])cnt++;
int l=1,r=n,ans=-1;//ans应置为-1,因为当不存在方案的时候,因该输出-1
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid))
{
ans=mid;
r=mid-1;//mid已经存到答案里面了,所以可以之间让r=mdi-1
}
else l=mid+1;
}
cout<<ans;
return 0;
}
思路:首先看到看到题意是求,最少用多少边,所以答案是具有单调性的,所以我们因该要想到二分。然后,我们再用并查集来统计每个集合的元素个数即可。
题目链接:https://www.luogu.com.cn/problem/P1419
题意:给一个序列a,让你找出这个序列的连续子序列的最大平均值。
#include<bits/stdc++.h>
using namespace std;
int a[100009],q[100009];
double b[100009],sum[100009];
double ans=0;
int s,t,n;
bool check(double x)
{
int l=1,r=0;
for(int i=1;i<=n;i++)b[i]=(double)a[i]-x;//每个a减去要判断的答案
sum[0]=0;
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+b[i];
for(int i=1;i<=n;i++)
{
if(i>=s)//因为要去了序列的长度必须在s到t之间
{
while(l<=r&&sum[i-s]<sum[q[r]])r--;//建立一个单调递增队列,单调队列里面存的是数组的下标
q[++r]=i-s;
}
if(l<=r&&q[l]<i-t)l++;//保证长度是在s到t之间
if(l<=r&&sum[i]-sum[q[l]]>=0)return true;
}
return false;
}
int main()
{
cin>>n;
cin>>s>>t;
for(int i=1;i<=n;i++)cin>>a[i];
double l=-10000,r=10000;
while(r-l>1e-5)//二分答案
{
double mid=(r+l)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%.3lf",l);
return 0;
}
思路:首先我们可以知道答案具有单调性,所以肯定会和二分有关系。一般看到单调性的问题我们一定要想到二分。我们只需二分答案,然后判断当前二分的答案是否正确即可。我们在判断是否正确的时候,只需让每个a[i]减掉我们要判断的答案,然后再去找有没有一段长度为s到t的序列,它的和大于0即可。所以,我们只需在i-t到i-s中找到一个最小值,然后判断sum[i]减去那个最小值是否大于0即可。