2018中国大学生程序设计竞赛 - 网络选拔赛
A.HDU6438 Buy and Resell
题意
给你N天N个价格,每天都可以从1.买入一个,2.卖出一个,3.什么都不做,求最高获利
低买高卖问题,这题与其他的差距就是要在满足获利最多的情况下,买卖次数最小;
思路
手算一下发现价格具有传递性;例如数据是1,5,9;
5的时候买入1赚了4;9的时候买入5赚了4;相当于直接把5当成跳板直接9的时候买1;然后再把中间的5当成没有买过的;
建立一个小根堆(优先队列);把当前遇到的天数的价钱放入,然后对于第i天;
1.如果新天数的价钱比堆顶小;说明交易亏本,直接丢入堆中;
2.如果价钱高说明有赚头;
2.1.如果堆顶的是已经跟之前的交易过的,那相当于当前天和之前的那个交易,然后堆顶的变成没有交易的;继续插入;交易次数不变
2.2否则直接交易,交易次数+2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e6+50;
const ll inf=0x3f3f3f3f3f3f;
int a[maxn];
struct node{
int id;
int money;
bool has;
node(int id,int money,bool has):id(id),money(money),has(has){}
bool friend operator <(node a,node b){
if(a.money==b.money)return a.has<b.has;
return a.money>b.money;
}
};
int flag[maxn];
int used[maxn];
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
ll ans=0;
ll cnt=0;
memset(flag,0,sizeof(flag));
memset(used,-1,sizeof(used));
for(int i=0;i<n;i++)cin>>a[i];
priority_queue<node>pq;
for(int i=0;i<n;i++){
if(pq.empty())pq.push(node(i,a[i],0));
else{
node now=pq.top();
if(now.money>=a[i])pq.push(node(i,a[i],0));
else{
pq.pop();
if(flag[now.id]){
ans+=a[i]-now.money;
flag[i]=1;flag[now.id]=0;
used[i]=used[now.id];
used[now.id]=-1;
pq.push(node(i,a[i],1));
pq.push(node(now.id,a[now.id],0));
}
else{
ans+=a[i]-now.money;
cnt+=2;
flag[i]=1;flag[now.id]=1;
used[i]=now.id;
pq.push(node(i,a[i],1));
}
}
}
}
cout<<ans<<" "<<cnt<<endl;
}
return 0;
}
C.HDU6440 Dream
题意
对一个质数P,对小于P的非负数定义一种乘法和加法运算,使得其满足封闭性,且对于存在一个数q使得{qk|0< k< p}
思路
费马小定理,比赛随便迷一样的试了一下就A了,自己也不知道怎么解释...
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e5+50;
const ll inf=0x3f3f3f3f3f3f;
int n;
int vis[maxn];
int size[maxn];
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int t;
cin>>t;
while(t--){
int p;
cin>>p;
for(int i=0;i<p;i++){
cout<<i;
for(int j=1;j<p;j++){
cout<<" "<<(i+j)%p;
}
cout<<endl;
}
for(int i=0;i<p;i++){
cout<<0;
for(int j=1;j<p;j++){
cout<<" "<<(i*j)%p;
}
cout<<endl;
}
}
return 0;
}
D.HDU6441 Find Integer
题意
题意:已知an+bn=c^n,给出n和a,求b,c,如果无解输出−1。
思路
费马大定理
- an+bn=c^n,n>2时无解。
- 当a为奇数时,
a=2⋅k+1
c=k2+(k+1)2
b=c−1
3.当 a 为偶数
a=2∗k+2
c=1+(k+1)^2
b=c−2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=3e6+50;
const ll inf=0x3f3f3f3f3f3f;
int main()
{
//std::ios::sync_with_stdio(false);
// std::cin.tie(0);
//std::cout.tie(0);
int t;
scanf("%d",&t);
while(t--){
ll n,a;
scanf("%lld%lld",&n,&a);
if(n==0){
printf("-1 -1\n");
}
else if(n==1){
printf("1 %lld\n",a+1);
}
else if(n==2)
{
if(a&1){
printf("%lld %lld\n",a*a/2,a*a/2+1);
}
else{
printf("%lld %lld\n",(a/2)*(a/2)-1,(a/2)*(a/2)+1);
}
}
else{
printf("-1 -1\n");
}
}
return 0;
}
I.HDU6446 Tree and Permutation
题意
题意:给你一颗树,然后让你求n!种序列中,所以得序列和,序列和定义为:A1,A2,A3……AN=A1A2+A2A3+…….An-1An
思路
首先,对于题目给出的n-1条边,我们可以这样考虑,去掉这条边后,将树分成了两部分,一部分有M个节点,另一部分有(N-M)个节点,所以我们必须在这两块中任意选择一个节点才会进过这条边,所以,有N × M× 2 中选择,然后又N!个序列所以对于E这条边,一共又2×N×M×(N-1)!×L的贡献。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e5+50;
const ll inf=0x3f3f3f3f3f3f;
int n;
int vis[maxn];
int size[maxn];
struct Edge{
int to;
int s;
int t;
int l;
Edge(int to,int s,int t,int l):to(to),s(s),t(t),l(l){}
};
ll f[maxn];
int init(){
f[0]=1;f[1]=1;
for(int i=2;i<=100000;i++)f[i]=(f[i-1]*i)%mod;
}
ll ans;
vector<Edge>G[maxn];
void dfs(int u){
vis[u]=1;
int cnt=1;
for(int i=0;i<G[u].size();i++){
Edge &e=G[u][i];
if(vis[e.to]==0){
dfs(e.to);
ll tmp=size[e.to];
tmp=tmp*(n-tmp)%mod;
tmp=tmp*2%mod;
tmp=tmp*e.l%mod;
tmp=tmp*f[n-1]%mod;
ans+=tmp;
ans%=mod;
cnt+=size[e.to];
}
}
size[u]=cnt;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int t;
init();
while(cin>>n){
for(int i=0;i<=n;i++)G[i].clear();
for(int i=0;i<=n;i++)size[i]=0,vis[i]=0;
ans=0;
for(int i=0;i<n-1;i++){
ll x,y,l;
cin>>x>>y>>l;
G[x].push_back(Edge(y,x,y,l));
G[y].push_back(Edge(x,x,y,l));
}
dfs(1);
cout<<ans<<endl;
}
return 0;
}
J.HDU6447 YJJ's Salesman
题意
题意:一个地图,里面有最多1e5个村庄,YJJ从0,0开始走,只能向左,下,左下走,如果向左下走进一个村庄那就能获得这个村庄的价值;求最大价值;从(0,0)到(n,n);
数据范围1e9!数组装不下
思路
首先题意是求最大值,最基本的DP模型是三个方向的最大值;但是复杂度会爆炸而且也开不了那么大的数组;
1.离散化坐标;因为1e9的坐标范围太大数组装不下,但是只有1e5的村庄,所以可以把1e9离散化到1e5里面;
2.每次搜索前面的最大值;利用树状数组搜索到当前位置的J坐标之前的最大值然后加上当前值再添加进树状数组
比赛的时候离散完,想吧坐标压缩成一维的可是没想到办法所以凉凉了,其实这题不用压缩直接按照x的坐标从小到大排序,然后按照同一行从大到小排序就行了;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e5+50;
const int maxm=1e5+50;
const ll inf=0x3f3f3f3f3f3f;
int N;
struct node{
int x,y,value;
};
int cmp(node a,node b){
if(a.x==b.x)return a.y>b.y;
return a.x<b.x;
}
int c[maxm];
inline int lowbit(int x){return x&(-x);}
void update(int x,int p){
for(int i=x;i<maxn;i+=lowbit(i)){
c[i]=max(c[i],p);
}
}
int query(int x){
int ma=0;
for(int i=x;i>0;i-=lowbit(i)){ma=max(ma,c[i]);}
return ma;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
vector<node>ve;
vector<int>k;
k.push_back(-1);
memset(c,0,sizeof(c));
for(int i=0;i<n;i++){
node a;
cin>>a.x>>a.y>>a.value;
ve.push_back(a);
k.push_back(a.y);
}
sort(k.begin(),k.end());
sort(ve.begin(),ve.end(),cmp);
k.erase(unique(k.begin(),k.end()),k.end());
N=k.size();
int anw=0;
for(int i=0;i<n;i++){
int ans=lower_bound(k.begin(),k.end(),ve[i].y)-k.begin();
int cnt=query(ans-1);
cnt+=ve[i].value;
update(ans,cnt);
anw=max(anw,cnt);
}
cout<<anw<<endl;
}
return 0;
}