B. Email from Polycarp
题解:
水题,练习一下STL,方便很多
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
int a[26];
int vis[26];
int n,m;
string str,stl;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int x,y,z,k,sum=0;
cin>>n;
while(n--)
{
cin>>str;
cin>>stl;
vector<pair<char,int> >s1,s2;
int flag=0;
int len=str.length();
int len1=stl.length();
for(int i=0;i<len;i++)
{
if(s1.empty()||s1.back().first!=str[i])
s1.push_back({str[i],0});
s1.back().second++;
}
for(int i=0;i<len1;i++)
{
if(s2.empty()||s2.back().first!=stl[i])
s2.push_back({stl[i],0});
s2.back().second++;
}
if(s1.size()!=s2.size())
cout<<"NO"<<endl;
else
{
for(int i=0;i<s1.size();i++)
{
if(s1[i].first!=s2[i].first)
{
cout<<"NO"<<endl;
flag=1;
break;
}
if(s1[i].second>s2[i].second)
{
cout<<"NO"<<endl;
flag=1;
break;
}
}
if(flag==0)
cout<<"YES"<<endl;
}
}
}
C2. Exam in BerSU (hard version)
题意:
给你n个数字,从左往右有序,问对于每个数字来说,从左往右(到这个数字)取一些数字,如果取得数字一定要包括这个数字,且这些数字的累加和不能超过m,求对于每个数字来说,它最少落下几个数字没取
WA代码:
C 1 C_1 C1能过, C 2 C_2 C2数据太大超时了。两个 s o r t sort sort重复排序太耗时
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];
int n,m;
struct note
{
int x,id;
}s[N];
bool cmp(note a,note b)
{
return a.x<b.x;
}
bool cmp1(note a,note b)
{
return a.id<b.id;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int x,y,z,k,sum=0,ans;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s[i].x;
s[i].id=i;
a[i]=a[i-1]+s[i].x;
}
for(int i=1;i<=n;i++)
{
if(m>=a[i])
cout<<0<<" ";
else
{
sort(s+1,s+i,cmp);
sum=0,ans=0;
for(int j=i-1;j>=1;j--)
{
sum+=s[j].x;
ans++;
if(a[i]-sum<=m)
{
cout<<ans<<" ";
break;
}
}
if(i!=n)
sort(s+1,s+i,cmp1);
}
// cout<
}
}
AC代码:版本一
因为数组中的数的范围为1-100所以可以用桶排序,节省排序花费的时间
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N];//桶排序
int n,m;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int x,y,z,k,sum=0,ans,need,cnt;
cin>>n>>m;
while(n--)
{
cin>>x;
cnt=0;
need=sum-m+x;
for(int i=100;i>=1&&need>0;i--)
{
ans=i*a[i];
if(ans>=need)
{
cnt+=(need+i-1)/i;//需要删除几个数字
break;
}
need-=ans;
cnt+=a[i];
}
sum+=x;//前缀和
a[x]++;
cout<<cnt<<" ";
}
}
AC代码:版本二
因为是要找到区间的从大到小的值,而且含有重复元素,想到可以使用multset
来自动排序,练习一下STL
根据题意理解,前缀和会越来越大,所以越往后重复删除的元素越多,所以从一开始就把以后一定会删除的元素删去,减少时间复杂度
所以优化就是,把次前缀和都删到刚好小于m,然后记录删除的个数。
证明为什么此时删掉的数以后一定会删掉:
1.如果后来进入容器的数字小于删掉的数字,根据从大到小删的原可得;
2.如果后来进入容器的数字不小于删掉的数字,那即使应该先删掉那个后来的数,但由于此时的前缀和仍大于m所以可得;
所以可以先删除,而不用考虑排序后再从大到小删除。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
multiset<int,greater<int> >cup;
int n,m;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int x,y,z,k,sum=0,ans,need,cnt,exc=0;
cin>>n>>m;
while(n--)
{
cin>>x;
cnt=0;
need=sum-m+x;
auto it=cup.begin();
while(need>0)
{
need-=*it;
it++;
cnt++;
}
sum+=x;
cup.insert(x);
cout<<cnt+exc<<" ";
while(sum>m)//优化,如果不优化还是会超时
{
sum-=*(cup.begin());
exc++;
cup.erase(cup.begin());
}
}
}
D. Extra Element
题解:
把全部的公差存到数组里,然后每个数删一遍,判断是否有唯一的工差,就可求应该删去的数字下标了,练习运用STL
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair
const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m;
pii a[N];
map<int ,int>maps;
void add(int x,int y)
{
maps[x]+=y;
if(maps[x]==0)
maps.erase(x);
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int x,y,z,k,sum=0,ans,need,cnt,exc=0;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i].first;
a[i].second=i;
}
sort(a,a+n);
for(int i=1;i<n;i++)
{
add(a[i].first-a[i-1].first,1);
}
for(int i=0;i<n;i++)
{
if(i==0)
add(a[i+1].first-a[i].first,-1);
else if(i==n-1)
add(a[i].first-a[i-1].first,-1);
else
{
add(a[i].first-a[i-1].first,-1);
add(a[i+1].first-a[i].first,-1);
add(a[i+1].first-a[i-1].first,1);
}
if((int)maps.size()<=1)
{
cout<<a[i].second+1<<endl;
return 0;
}
if(i==0)
add(a[i+1].first-a[i].first,1);
else if(i==n-1)
add(a[i].first-a[i-1].first,1);
else
{
add(a[i].first-a[i-1].first,1);
add(a[i+1].first-a[i].first,1);
add(a[i+1].first-a[i-1].first,-1);
}
}
cout<<-1<<endl;
}
让我想起了一道之前做过的月赛题
/*
Description
这天小g遇到了一个队列,小g觉得队列乱糟糟的不好看。于是小g希望将队列调整成为一个等差数列
(差可为0)。但是小g对每个数都最多调整一次,每次可以对这个数加一、减一。请你帮助小g解决这
个问题,如果能调整出等差队列,输出需要调整的数的最小数量,否则输出-1。
Input
第一行一个整数n(2 <= n <= 100000),表示数列中数的个数;
第二行为n个整数pi (1 <= pi <= 1e9)。
Output
输出一个整数,表示操作数量的最小值。如果不存在则输出-1。
*/
#include
using namespace std;
const int MAX_N=3e5+9;
const int INF=1e9+9;
int vec[MAX_N];
int res[MAX_N];
int N,M,T,S;
queue<int> que;
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(cin>>N)
{
for(int i=0;i<N;i++)
{
scanf("%d",&vec[i]);
}
if(N==1 ||N==2)
{
cout<<0<<endl;
continue;
}
int ax=INF;
for(int a=0;a<3;a++)
{
for(int b=0;b<3;b++)
{
int t=vec[1]+b-1-(vec[0]+a-1);//全排列,公差
int pre=vec[1]+b-1;//第一个数
bool f=true;
int ans=abs(a-1)+abs(b-1);//改变次数
//cout<
for(int i=2;i<N;i++)
{
int temp=vec[i]-pre;//每项的公差
if(abs(temp-t)>1)//如果不相等且差值大于一就终止
{
//cout<
f=false;
break;
}
ans+=abs(pre+t-vec[i]);//改变次数
pre=pre+t;
}
if(f) ax=min(ax,ans);
}
}
if(ax!=INF)cout<<ax<<endl;
else cout<<-1<<endl;
}
}
E. Polycarp and Snakes
题解:
模拟就行了,记住,Polycarp是按字母顺序画蛇的。
首先,我们应该找出每个字母的左上角和右下角出现的最多的地方。
其次,我们应该通过这些字母从“z”到“a”。我们先跳过未找到的字母。如果任何字母的长度和宽度都大于1,就没有办法画蛇。否则,我们应该检查行中的所有元素是否等于当前字母或’’。如果是这样,让我们用“”把这条线划掉,然后继续看下一个字母。如果不是这样,就没有办法画蛇。
如果有答案,对于每条蛇,我们可以输出相关字母的最左上角和最右下角的坐标。如果某个字母没有出现,我们可以假设下一个字母完全覆盖了当前的字母。我们完全可以解决这个问题,通过不超过1+26=27个领域走。
代码太长不想写了…
B. Split a Number
模拟题,不过写起来好麻烦…
以下代码为转载:
#pragma GCC optimize(2)
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
//typedef __int128 lll;
ll n,m,k;
ll ans;
string s,t,w,x,y;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
cin>>s;
n=s.size();
reverse(s.begin(),s.end());//便于计算数据相加
//x和y分别是左半端和右半端的最优解
for(int i=n/2;i>=0;i--){
x="";
t=s.substr(0,i);
w=s.substr(i,n-i);
if(w[w.size()-1]=='0'||t[t.size()-1]=='0'||t.empty()||w.empty())continue;//跳过前导0和空串
x=w;
for(int i=0;i<t.size();i++){//高精加法
x[i]+=t[i]-'0';
}
for(int i=0;i<w.size()-1;i++){
while(x[i]>'9')x[i]-=10,x[i+1]++;//进位
}
if(x[x.size()-1]>'9')x[x.size()-1]-=10,x+='1';//进位
//cout<
break;//因为找到一个解以后,它就是最优解,所以直接break
}
for(int i=(n+1)/2;i<n;i++){
y="";
t=s.substr(i,n-i);
w=s.substr(0,i);
if(w[w.size()-1]=='0'||t[t.size()-1]=='0'||t.empty()||w.empty())continue;
y=w;
for(int i=0;i<t.size();i++){
y[i]+=t[i]-'0';
}
for(int i=0;i<w.size()-1;i++){
while(y[i]>'9')y[i]-=10,y[i+1]++;
}
if(y[y.size()-1]>'9')y[y.size()-1]-=10,y+='1';
//cout<
break;
}
//比较
reverse(x.begin(),x.end());//重置数组
reverse(y.begin(),y.end());
if(x.empty()){
cout<<y;
return 0;
}
if(y.empty()){
cout<<x;
return 0;
}
if(x.size()!=y.size()){cout<<(x.size()<y.size()?x:y);return 0;}
else{
for(int i=0;i<x.size();i++){
if(x[i]!=y[i]){
cout<<(x[i]<y[i]?x:y);
return 0;
}
}//cout<
cout<<x;
}
return 0;
}
题意:有一个三岁的小屁孩特别喜欢三个数组成的等差数列,他现在有一组数,特别想知道里面含多少由三个数组成的等比数列,他年纪太小不会算,想难为一下搞ACM的。注意 可以不连续,但是等比数列在数组中的位置必须是递增的。
思路:遍历数组,对于每一个值都把他当做等比数列的中间的值x,对K求余,余数为零时,看(x/k)在该数之前出现了多少次,(xk)在该数之后出现了多少次,求和即可。
现在问题转换成,在x之前 x/k,xk 出现了多少次?
用map数组处理一下左右的数出现了多少次,在遍历数组的时候,每到一个数mapR[x]–;处理完之后mapL[x]++;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mem(a) memset(a,0,sizeof(a));
#define mem_1(a) memset(a,-1,sizeof(a));
#define sf(a) scanf("%d",&a)
#define sff(a,b) scanf("%d%d",&a,&b)
#define sfff(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define lson l,mid,i<<1
#define rson mid+1,r,i<<1|1
#define LL long long
const int INF = 0x7FFFFFFF;
const int MAXN = 1010000;
const double PI = acos(-1.0);
const double esp = 1e-10;
using namespace std;
map<LL,LL> L;
map<LL,LL> R;
LL data[MAXN];
int main()
{
LL n,k;
scanf("%I64d %I64d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%I64d",&data[i]);
R[data[i]]++;
}
LL ans = 0;
for(int i=1;i<=n;i++)
{
R[data[i]]--;
if(data[i]%k==0)
{
ans += L[data[i]/k] * R[data[i]*k];
}
L[data[i]]++;
}
printf("%I64d\n",ans);
return 0;
}
佛系刷题…