travel ——3814
思路:显然的dp,但显然有点贪心的味道。
首先,说一下O(n3)的暴力dp:dp[i][j] 表示走到第i个路段时,上一个路段的高度为j,转移也十分容易,dp[i][j]=min(dp[i−1][k]+abs(A[i]−k)+abs(k−j),dp[i][j])。
这里,显然第一维除了空间上可以滚动,时间上是肯定不能优化了,而j的高度范围也很大,这就使人不难去猜想每个路段之间的高度关系。
再仔细思考,一定是在出现峰谷时才使用魔法,也就是满足A[i]>A[i-1]&&A[i]>A[i+1] || A[i]<A[i-1] && A[i]<A[i+1]。
那么发现这就是一道贪心题了,每次修改高度,加上高度差或消耗魔法值即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,sizeof(a))
#define mcp(a,b) memcpy(a,b,sizeof(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define N 100005
int n;
LL A[N];
struct p40{
LL dp[405][405];
void solve(){
mcl(dp,127);
REP(i,0,400)dp[0][i]=0;
REP(i,1,n){
REP(j,0,400)dp[i][A[i]]=min(dp[i][A[i]],dp[i-1][j]+abs(j-A[i]));
REP(j,0,400)
REP(k,0,400)
dp[i][k]=min(dp[i][k],dp[i-1][j]+abs(j-k)+abs(A[i]-k));
}
LL ans=1e15;
REP(i,0,400)ans=min(ans,dp[n][i]);
cout<<ans<<endl;
}
}p40;
struct p100{
void solve(){
LL ans=0;
REP(i,2,n){
if(A[i]>A[i-1] && A[i]>A[i+1]){
ans+=abs(A[i]-max(A[i-1],A[i+1]));
A[i]=max(A[i-1],A[i+1]);
}
else if(A[i]<A[i-1] && A[i]<A[i+1]){
ans+=abs(A[i]-min(A[i-1],A[i+1]));
A[i]=min(A[i-1],A[i+1]);
}
}
REP(i,2,n)ans+=abs(A[i]-A[i-1]);
cout<<ans<<endl;
}
}p100;
int main(){
// freopen("travel.in","r",stdin);
// freopen("travel.out","w",stdout);
cin>>n;
REP(i,1,n)scanf("%lld",&A[i]);
if(n<=400)p40.solve();
else p100.solve();
return 0;
}
santa ——3815
思路:经典的树上算平均距离,原题是2个点的平均距离,而此题是3个点。
其实也是一样的。一遍dfs算出每个点对边的贡献,最后再处理边数(然而,这里考试时没算好)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define db double
#define pb push_back
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,sizeof(a))
#define mcp(a,b) memcpy(a,b,sizeof(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1
#define N 500005
int n;
struct node{
int to,cost;
};
vector<node>E[N];
struct p40{
#define M 1005
int dis[M][M];
void solve(){
mcl(dis,63);
REP(i,1,n-1){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
dis[a][b]=dis[b][a]=c;
}
REP(k,1,n)
REP(i,1,n)
REP(j,1,n)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
db ans=0,cnt=0;
REP(i,1,n){
REP(j,i+1,n){
REP(k,j+1,n){
ans+=dis[i][j]+dis[i][k]+dis[k][j];
cnt++;
}
}
}
printf("%.2lf\n",ans/cnt);
}
}p40;
struct p100{
db ans;
int sz[N];
LL C[5];
void dfs(int x,int f){
sz[x]=1;
REP(i,0,E[x].size()-1){
node y=E[x][i];
if(y.to==f)continue;
dfs(y.to,x);
sz[x]+=sz[y.to];
ans+=1.0*y.cost*sz[y.to]*(n-sz[y.to])*(n-2)/C[3];
}
}
void solve(){
C[0]=1;
REP(i,1,3)C[i]=C[i-1]*(n-i+1)/i;
REP(i,1,n-1){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
E[a].push_back((node){b,c});
E[b].push_back((node){a,c});
}
ans=0;
dfs(1,0);
printf("%.2lf\n",ans);
}
}p100;
int main(){
// freopen("santa.in","r",stdin);
// freopen("santa.out","w",stdout);
cin>>n;
if(n<3){
REP(i,1,n-1){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
}
printf("%.2lf\n",(db)0);
}
else if(n<=100)p40.solve();
else p100.solve();
return 0;
}
set ——3816
思路:其实这题不难,真心不难,集合是有规律的,除(n=3外,此时集合的大小恒为3),每次[L,R]一定会变成[2*L+1,2*R-1],那么就可以去找k是否在这个区间,发现这个后,这题就是一个高精度模板题。(贴一下模板…)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define db double
#define pb push_back
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,sizeof(a))
#define mcp(a,b) memcpy(a,b,sizeof(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1
#define P 100000000
#define N 40005
int n,m;
struct Big{
LL num[N],len;
Big(){
memset(num,0,sizeof(num));
len=1;
}
Big operator +(const Big &a)const{
Big b;
b.len=max(len,a.len);
REP(i,0,b.len-1){
LL &B=b.num[i];
B+=num[i]+a.num[i];
if(B>=P)B-=P,b.num[i+1]++;
}
if(b.num[b.len])b.len++;
return b;
}
Big operator -(const Big &a)const{
Big b;
REP(i,0,len-1)b.num[i]=num[i];
b.len=len;
DREP(i,a.len-1,0){
b.num[i]-=a.num[i];
int now=i;
while(b.num[now]<0)b.num[now]+=P,now++,b.num[now]--;
}
while(b.len>1 && !b.num[b.len-1])b.len--;
return b;
}
Big operator *(const Big &a)const{
Big b;
b.len=a.len+len-1;
REP(i,0,len-1){
REP(j,0,a.len-1){
LL &B=b.num[i+j];
B+=num[i]*a.num[j];
if(B>=P)b.num[i+j+1]+=B/P,B%=P;
}
}
while(b.num[b.len])b.len++;
return b;
}
Big operator /(const int &a)const{
Big b;
REP(i,0,len-1)b.num[i]=num[i];
DREP(i,len-1,1)b.num[i-1]+=b.num[i]%a*P,b.num[i]/=a;
b.num[0]/=a;
b.len=len;
while(b.len>1 && !b.num[b.len-1])b.len--;
return b;
}
void Rd(){
char A[N];
scanf("%s",A);
int SL=strlen(A);
len=0;
for(int i=SL-1,res;res=0,i>=0;num[len++]=res,i-=8){
if(i>=7)for(int j=i-7;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
else for(int j=0;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
}
}
void Pr(){
printf("%lld",num[len-1]);
DREP(i,len-2,0)printf("%08lld",num[i]);
}
void jia(){
num[0]++;
REP(i,0,len-1){
if(num[i]>=P)num[i]-=P,num[i+1]++;
else break;
}
if(num[len])len++;
}
void jian(){
num[0]--;
REP(i,0,len-1){
if(num[i]<0)num[i]+=P,num[i+1]--;
else break;
}
while(!num[len-1] && len>0)len--;
}
bool operator<=(const Big &a)const{
if(len!=a.len)return len<a.len;
DREP(i,len-1,0)
if(num[i]!=a.num[i])return num[i]<a.num[i];
return 1;
}
};
struct p3{
void solve(){
Big a,ans;
a.num[0]=2;
ans.num[0]=1;
scanf("%d",&m);
int b=(m-1)/3+1;
while(b){
if(b&1)ans=ans*a;
b>>=1;
if(!b)break;
a=a*a;
}
if(m%3==1)ans.jian();
else if(m%3==0)ans.jia();
ans.Pr();
}
}p3;
struct p100{
void solve(){
Big L,R,k,sum,ans;
k.Rd();
L.num[0]=1;
R.num[0]=n;
sum=R-L;
sum.jia();
if(k<=sum){
ans=L+k;
ans.jian();
ans.Pr();
}
else {
k=k-sum;
while(1){
L=L+L;R=R+R;
L.jia();R.jian();
sum=R-L;
sum.jia();
if(k<=sum)break;
else k=k-sum;
}
ans=L+k;
ans.jian();
ans.Pr();
}
}
}p100;
int main(){
// freopen("set.in","r",stdin);
// freopen("set.out","w",stdout);
scanf("%d",&n);
if(n<3){
scanf("%d",&m);
if(n==1){
if(m==1)puts("1");
else puts("-1");
}
else if(n==2){
if(m==1)puts("1");
else if(m==2)puts("2");
else if(m==3)puts("3");
else puts("-1");
}
}
else if(n==3)p3.solve();
else p100.solve();
return 0;
}
小结:今天的考试题目真心不难,会想一下,考试时都想得不深入,很浅,而且还是有点急了(当时第2题暴力folyd迷之打错,就很慌,无奈写了lca…(给小c喷死)),最后竟然还有时间一直在检查第1题…——时间还是要看题目而分配呀。