1490E. Accidental Victory(二分)
题意:
一共n个人参加比赛,每个人有一个强壮值。
每次随机挑选两人pk,赢的人获得另一人的强壮值。如果强壮值相同,随机赢。
问,最终有哪些人有赢的可能性?
思路:
也就是问,有哪些人必输了。
一个人所得的到的最大强壮值为比其小的所有强壮值之和。
法1:
将所有人从小到大排序。
对于一个位置的前缀和如果不少于下一位置的值,说明当前位置可以胜过下一位置。
如果不能,说明当前位置以及前面所有位置都没有赢的可能。
找出最后一个不会赢的位置。
法2:二分
因为当前位置如果有赢的可能的话,后面的位置一定能赢,所以可以二分第一个能赢的位置。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m;
PII a[N];
bool check(int mid)
{
ll sum=a[mid].first;
for(int i=1;i<=n;i++)
{
if(i==mid) continue;
if(a[i].first<=sum) sum+=a[i].first;
else return 0;
}
return 1;
}
int main(){
Ios;
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].first;
a[i].second=i;
}
sort(a+1,a+n+1);
/*法1:
ll sum=0,f=0;
for(int i=1;i<n;i++)
{
sum+=a[i].first;
if(sum<a[i+1].first) f=i;
set<int> st;
for(int i=f+1;i<=n;i++) st.insert(a[i].second);
cout<<st.size()<<endl;
for(auto it:st){
cout<<it<<" ";
}
cout<<endl;
}*/
int l=1,r=n;
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
set<int> st;
for(int i=l;i<=n;i++) st.insert(a[i].second);
cout<<st.size()<<endl;
for(auto it:st){
cout<<it<<" ";
}
cout<<endl;
}
return 0;
}
448B. Suffix Structures(双指针)
题意:
给定两个字符串a,b,分别判断下列操作是否a能够变为b:
1.只交换两个字符的位置。
2.只删除某些字符。
3.同时使用前面两种操作。
思路:
判断只删除的时候,需要双指针判断b串在a串中,相对顺序不变的出现过。
只交换,判断是否出现次数相同。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m;
string a,b;
int main(){
cin>>a>>b;
n=a.size(),m=b.size();
a=" "+a,b=" "+b;
int cnt=0;
for(int i=1,j=1;i<=m;i++)
{
while(j<n&&a[j]!=b[i]) j++;
if(a[j]==b[i]) cnt++,j++;
}
if(cnt==m&&n!=m){
cout<<"automaton";return 0;
}
for(int i=1;i<=n;i++) mp[a[i]]++;
bool flag=0;
for(int j=1;j<=m;j++){
if(!mp[b[j]]){
flag=1;break;
}
mp[b[j]]--;
}
if(flag){
cout<<"need tree";
return 0;
}
if(n==m){
cout<<"array";
}
else cout<<"both";
return 0;
}
1462D. Add to Neighbour and Remove(思维,枚举答案)
题意:
给定一个长度为 n 的数列,问最少多少次下述操作,各位置元素相等?
选一个位置i,将ai的值加到位置 i-1 或 i+1 上;将位置i删除,后面的元素递补。
思路:
其实,上面的操作其实就是将若干个连续的位置合并为1个位置,变成一个新的数列。
让合并后的所有元素相同。所以我们可以枚举最后所有元素变成的值。
但是整个范围太大了。
但是 1~i 个位置肯定是要合并为1个位置的,所以我们可以枚举第一组合并的位置个数i。
让这组的值作为最后所有元素的值,判断是否能行通。如果能,答案取最小操作数。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
ll sum;
int ans;
void check(int m)
{
sum=0;
for(int i=1;i<=m;i++) sum+=a[i];
ll t=0;int cnt=m-1;
for(int i=m+1;i<=n;i++)
{
t+=a[i];cnt++;
if(sum==t) t=0,cnt--;
else if(t>sum) return;
}
if(t!=0) return;
ans=min(ans,cnt);
}
int main(){
Ios;
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
ans=1e9;
for(int len=1;len<=n;len++)
{
check(len);
}
cout<<ans<<endl;
}
return 0;
}
650A. Watchmen(组合)
题意:
给出若干个点,判断有多少对点的两种距离的求算方式所得的结果相同?
思路:
化简等式得,(xi-xj)(yi-yj)=0
。
所以满足方式的点对,要么x相同,要么y相同,要么都相同。
所以可以求出满足x相同的点对数+y相同的点对数,再减去x和y都相同的点对数。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
int main(){
Ios;
cin>>n;
for(int i=1;i<=n;i++){
int p,q;cin>>p>>q;
x[p]++,y[q]++;
mp[{p,q}]++;
}
ll ans=0;
for(auto it:x){
ll cnt=it.second;
ans+=cnt*(cnt-1)/2;
}
for(auto it:y){
ll cnt=it.second;
ans+=cnt*(cnt-1)/2;
}
for(auto it:mp){
ll cnt=it.second;
ans-=cnt*(cnt-1)/2;
}
cout<<ans;
return 0;
}
这时候想起来先变形公式了。。
1380B. Universal Solution(思维)
题意:
给定一个字符串a,三种字符:RSP,对应石头、剪刀、布。
可以从一个位置开始循环,输出。
构造一个字符串b,从1开始输出,使得a串分别从n个位置开始循环,通过字符串b顺序输出所得的赢的次数的平均值最大。
思路:
构造字符串b的所有字符为a串中出现最多的字符的敌人。
每次都收割a串中出现次数多的字符的个数。
大胆假设!!
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m;
string a;
int main(){
Ios;
cin>>T;
while(T--)
{
cin>>a;
int cnt1=0,cnt2=0,cnt3=0;
for(int i=0;i<a.size();i++)
{
if(a[i]=='R') cnt1++;
else if(a[i]=='S') cnt2++;
else cnt3++;
}
int maxa=max(cnt1,max(cnt2,cnt3));
char c;
if(cnt1==maxa) c='P';
else if(cnt2==maxa) c='R';
else c='S';
for(int i=0;i<a.size();i++) cout<<c;
cout<<endl;
}
return 0;
}
1451C. String Equality(思维)
题意:
给定长度为n的字符串a,b。给定长度m。通过下面的操作,判断串a能否变换成串b?
-
交换两个字符的位置。或
-
选择长度为m的,全部元素都相同的子串,将其全部元素+1。(例’b’变为’c’)
思路:
因为位置能随意变换,所以连续的元素相同的位置不用管。
分别统计出串a、串b中每个元素出现的次数。
遍历串a中每种元素,判断其个数是否大于m:
如果是,就可以将b中的第一个比其大的字符的个数消去m个;
如果不是,就需要b中有一个和该字符相同的字符。如果没有,不合要求。
最后判断是否b中的元素都被消掉了,个数都为0。如果不是,不合要求。
Code:
const int N = 1000010, mod = 1e9+7;
int T, n, m;
char a[N],b[N];
char c[N],d[N];
int main(){
Ios;
cin>>T;
while(T--)
{
mp1.clear();mp2.clear();
cin>>n>>m;
cin>>a+1;
cin>>b+1;
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for(int i=1;i<=n;i++) mp1[a[i]]++;
for(int i=1;i<=n;i++) mp2[b[i]]++;
int ans=0;
for(auto it:mp1)
{
while(it.second>=m)
{
bool flag=0;
for(int i=it.first-'a';i<26;i++)
{
char c='a'+i;
if(mp2[c]>=m){
mp2[c]-=m;
flag=1;
break;
}
}
if(!flag){
ans=-1;break;
}
it.second-=m;
}
if(ans==-1) break;
if(it.second)
{
if(mp2[it.first]>=it.second) mp2[it.first]-=it.second;
else{
ans=-1;break;
}
}
}
for(auto it:mp2){
if(it.second) ans=-1;
}
if(ans==-1) cout<<"No\n";
else cout<<"Yes\n";
}
return 0;
}