D
题意:统计1~N中,k的个数为多少。
k要满足:k=
p
∗
q
3
p*q^3
p∗q3,且p<q,p,q均为质数。
先把 1 0 6 10^6 106内的质数全部筛出来,然后直接暴力统计即可,注意要防止溢出。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
const int N=2e6+10,mod=998244353;
int p[N], cnt;
bool st[N];
void get_p(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (st[i]) continue;
p[++cnt] = i;
for (int j = i + i; j <= n; j += i)
st[j] = true;
}
}
void work()
{
int n;
cin>>n;
get_p(1000010);
map<int,int> mp;
//cout<<cnt<<endl;
int ans=0;
for(int i=1;i<=cnt;i++){
for(int j=i+1;j<=cnt;j++){
int x=p[i]*p[j]*p[j];
if(x>n/p[j]) break;
ans++;
}
}
cout<<ans<<endl;
}
signed main()
{
int t;
//cin>>t;
t=1;
while(t--)
{
work();
}
return 0;
}
E
给定两个序列A,B,有q次询问,每次询问问A的前x个元素和B的前y个元素,其组成的两个集合是否相同,也就是数字的种类是否相同。
利用哈希,对序列进行hash,每次判断其前缀的值是否相同。注意hash的方法,若hash方法不对的话会被卡。
可以使用无符号整数来自然溢出,也可取模。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
#define ull unsigned long long
const int N=1e6+10;
const int mod = 1e9 + 7;
const int P = 13331;
int a[N],b[N];
ull h1[N],h2[N];
void work()
{
int n,q;
cin>>n;
set<int> s;
for(int i=1;i<=n;i++){
cin>>a[i];
if(!s.count(a[i])){
h1[i] = (h1[i - 1] +(a[i]+mod)*a[i]);
}
else h1[i]=h1[i-1];
s.insert(a[i]);
}
s.clear();
for(int i=1;i<=n;i++){
cin>>b[i];
if(!s.count(b[i])){
h2[i] = (h2[i - 1] + (b[i]+mod)*b[i]);
}
else h2[i]=h2[i-1];
s.insert(b[i]);
}
cin>>q;
while(q--)
{
int x,y;
cin>>x>>y;
if(h1[x]==h2[y]) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
signed main()
{
int t;
//cin>>t;
t=1;
while(t--)
{
work();
}
return 0;
}
F
题意:给定n个点,构成一张披萨,你可以选择三个点,把它们构成的面积给吃掉,若设披萨总面积为s,吃掉的面积为b,求min(
∣
2
s
−
8
b
∣
\vert{2s-8b}\vert
∣2s−8b∣)。
计算几何问题,很明显可以一层循环枚举P,另一层枚举Q,然后加上P,Q,Q+1构成的面积,时间显然不允许。
正确做法是使用双指针,若其面积已经不满足条件,则减去P,Q,Q+1的面积,然后P向后移,而Q不需要重新退回。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> PII;
const int N=4e5+10;
PII a[N];
PII operator-(PII a,PII b)
{
return {b.x-a.x, b.y-a.y};
}
int chaji(PII a,PII b)
{
return a.x*b.y-a.y*b.x;
}
int area(PII a,PII b,PII c)
{
return abs(chaji(b-a,c-a)); //注意不能随意改变顺序
}
void work()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
a[i]={x,y};
}
int s=0;
for(int i=3;i<=n;i++){
s+=area(a[1],a[i-1],a[i]);
}
int nw=0,ans=9e18;
for(int i=1,j=2;i<=n;i++){//双指针,很像滑动窗口
while(nw*4<s){
nw+=area(a[i],a[j],a[j%n+1]);
ans=min(ans,abs(s-4*nw));
j=j%n+1;
}
nw-=area(a[j],a[i],a[i%n+1]);//注意下标
ans=min(ans,abs(s-4*nw));
}
cout<<ans<<endl;
}
signed main()
{
int t;
//cin>>t;
t=1;
while(t--)
{
work();
}
return 0;
}
G
题意:给定序列P,在第 i 天买进股票的话,需要
P
i
P_i
Pi元,若要卖出股票的话,会获得
P
i
P_i
Pi元,在任意一天,你可以做下面三种之一:
买进股票,卖出股票(如果有的话),什么都不做。
注意,当结束后若还有股票则股票作废。
问最多能获得多少钱。
做法是很经典的反悔贪心 ,就是给每个贪心的选择一个反悔的余地,从而来修正结果的正确性。具体解释见代码。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
const int N=1e6+10;
int a[N];
void work()
{
/*
反悔贪心
正常贪心是如果当前的价格比所持有股票最低价格高就卖
反悔就是,如果后来又遇到了更高的价格,就反悔,在这天卖出
比如某几天价格为 p<q<r ,贪心就是以p的价格买入,以q的价格卖出
反悔贪心可以再以q的价格买入,以r的价格卖出,注意实际上这样是不允许的
只是可以看成是这样的过程,实际的操作是不以q的价格卖出,而是等到r再卖
代码部分也用这种操作来达到反悔的目的(即在同一天卖完又买)
*/
priority_queue<int,vector<int>,greater<int> > q;
int n;
cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(q.size()&&q.top()<x){
ans+=x-q.top(); //先贪心的卖出
q.pop();
q.push(x); //push进来,用于后面的反悔
}
q.push(x);
}
cout<<ans<<endl;
}
signed main()
{
int t;
//cin>>t;
t=1;
while(t--)
{
work();
}
return 0;
}