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.如果堆頂的是已經跟之前的交易過的,那相當於當前天和之前的那個交易,然後堆頂的變成沒有交易的;繼續插入;交易次數不變
#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
題意
題意:已知a^n+b^n=c^n,給出n和a,求b,c,如果無解輸出?1。
思路
費馬大定理
- a^n+b^n=c^n,n>2時無解。
- 當a為奇數時,
a=2?k+1
c=k^2+(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;
}
2018中國大學生程序設計競賽 - 網絡選拔賽