总结
第六次训练成绩不太好,这是一场完全比拼手速地题目,这方面我不在行,想得比别人慢,敲代码也比别人慢一点,输了。
A - Buy and Resell (set+贪心)
description
从1走到n,每个点有一个值ai,可以选择在i点+a[i]或-a[i]或不操作,问最后地最大和是多少以及达到最大和所需地最少操作次数(n<=1e5,sum n<=5e5)
solution
这个模型很经典,但我仍想了很久……
我们考虑维护一个买入队列和卖出队列,每遇到一个a[i],设买入最小为x,卖出最小为y,若x<y<a[i],我们就把x卖出并把a[i]加入到卖出集,若y<x<a[i],则把y卖出,这里相当于在原来的地方不卖了转到这里卖,并把a[i]加入到卖出集,否则地话就把x买入并加入到买入集。最后把没卖出的东西撤销买入操作即可
code
#include<bits/stdc++.h>
#define ll long long
#define rp(i,a,b) for(ll i=a;i>=b;i--)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
using namespace std;
const ll maxn=1e5+7;
ll a[maxn],d[maxn];
ll n,m,i,t,j,k,l,x,y,z,T,ans,tim,num,num1;
multiset<ll>f;
multiset<ll>g;
ll read(){
char ch=getchar();ll x=0;
while (ch<48 || ch>57) ch=getchar();
while (ch>=48 && ch<=57) x=x*10+ch-48,ch=getchar();
return x;
}
int main(){
scanf("%lld",&T);
while (T--){
scanf("%lld",&n);
fo(i,1,n)a[i]=read();
ans=tim=0;f.clear();g.clear();num=num1=0;
fo(i,1,n){
x=(num)?(*f.begin()):0;
y=(num1)?(*g.begin()):0;
if (x && x<a[i] && (!y || y>x)){
ans+=a[i];tim++;
f.erase(f.begin());num--;
g.insert(a[i]);num1++;
}else if (y && y<a[i]){
ans+=a[i]-2*y;
g.erase(g.begin());
g.insert(a[i]);
f.insert(y);num++;tim++;
}else{
ans-=a[i];tim++;
f.insert(a[i]);num++;
}
}
while (num--)
ans+=*f.begin(),f.erase(f.begin()),tim--;
printf("%lld %lld\n",ans,tim);
}
}
C - Dream (欧拉定理)
description
重新构建0-p-1内乘法表和加法表使得 ( m + n ) p = m p + n p (m+n)^p=m^p+n^p (m+n)p=mp+np(p是质数)
solution
被队友坑了一把,他告诉我这题很怪,我刚开始一想这不是欧拉定理吗?他这么强肯定想到了,所以不可能是……
结果还真是……
直接对加法或乘法取模即可……
code
#include<bits/stdc++.h>
#define ll long long
#define rp(i,a,b) for(ll i=a;i>=b;i--)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
using namespace std;
const ll maxn=2000;
int a[maxn][maxn];
ll n,m,i,t,j,k,l,x,y,z,T,ans,tim,num,num1;
ll read(){
char ch=getchar();ll x=0;
while (ch<48 || ch>57) ch=getchar();
while (ch>=48 && ch<=57) x=x*10+ch-48,ch=getchar();
return x;
}
int main(){
//freopen("data.in","r",stdin);
T=read();
while (T--){
n=read();
fo(i,0,n-1){
fo(j,0,n-1){
t=(i+j)%n;
printf("%d ",t);
}
putchar('\n');
}
fo(i,0,n-1){
fo(j,0,n-1){
t=(i*j)%n;
printf("%d ",t);
}
putchar('\n');
}
}
}
D - Find Integer (费马大定理)
description
给定a,n,找出 a n + b n = c n a^n+b^n=c^n an+bn=cn的一组解
solution
根据费马大定理,n>2时无解……
code
#include<bits/stdc++.h>
using namespace std;
int read(){
int f=1,s=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())s=s*10+c-48;
return f*s;
}
void work(){
int n=read(),a=read();
if(n>2){
printf("-1 -1\n");
return;
}
if(n==0){
printf("-1 -1\n");
return;
}
if(n==1){
printf("1 %d\n",a+1);
return;
}
int b,c;
if(a&1){
c=(a*a+1)/2;
b=c-1;
}else{
c=(a*a/2+2)/2;
b=c-2;
}
printf("%d %d\n",b,c);
}
int main(){
int T=read();
while(T--)work();
}
G- Neko’s loop (模拟)
description
有n个点的环,每个点上有值ai,每次可以从i跳到(i+k)%n点,且每到达一个点可以收获这个点上的a[i],现在从任意一个点开始,问最多跳m步后的比s最小差多少(sum n<=5e5,n<=1e4)
solution
找寻环节,然后维护一个单调队列更新即可,由于每个店最多访问一次,所以复杂度o(N)
code
#include<bits/stdc++.h>
#define ll long long
#define rp(i,a,b) for(ll i=a;i>=b;i--)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
using namespace std;
const ll maxn=3e4+5;
ll a[maxn],b[maxn],bz[maxn],d[maxn];
ll n,m,s,p,i,t,j,k,l,x,y,z,T,ans,tim,num,num1,mx,q;
multiset<ll>f;
ll read(){
char ch=getchar();ll x=0;
while (ch<48 || ch>57) ch=getchar();
while (ch>=48 && ch<=57) x=x*10+ch-48,ch=getchar();
return x;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
T=read();
while (++q<=T){
scanf("%lld%lld%lld%lld",&n,&s,&m,&p);ans=0;
fo(i,0,n-1)scanf("%lld",&a[i]),bz[i]=0;
fo(i,0,n-1){
if (bz[i]) continue;
d[0]=0;
j=i;k=0;
while(!bz[j])
k++,bz[j]=1,d[k]=d[k-1]+a[j],j=(j+p)%n;
if (m>=k)l=m%k+k,t=((d[k]>0)?d[k]:0)*(m/k-1);
else t=0,l=m;
mx=0;
fo(i,k+1,3*k) d[i]=d[i-k]+d[k];
f.clear();
fo(i,0,l-1) f.insert(d[i]);
fo(i,l,l+k){
mx=max(mx,d[i]-(*f.begin()));
f.insert(d[i]);
f.erase(f.find(d[i-l]));
}
ans=max(ans,t+mx);
}
ans=(s>ans)?s-ans:0;
printf("Case #%lld: %lld\n",q,ans);
}
}
I - Tree and Permutation (树形dp)
description
一棵有n(<=1e5)个点的树,现在任意排一个长度为n的序列,每个点仅出现1次(有n!种方案),每一种排列的代价为序列中相邻两点在树上的最短距离的和,问所有方案的代价和是多少
solution
对于点i,j的最短距离di,j,他们被考虑到的情况在所有方案中的贡献为2*(n-1)*di,j,所以答案就是 2 ∗ ( n − 1 ) ∑ d i , j 2*(n-1)\sum{di,j} 2∗(n−1)∑di,j
code
#include<bits/stdc++.h>
using namespace std;
int read(){
int f=1,s=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())s=s*10+c-48;
return f*s;
}
struct qq{
int v,w;
};
long long ans;
const int MOD=1e9+7;
const int N=1e5+10;
vector<qq>a[N];
int size[N],n;
void dfs(int u,int fa){
size[u]=1;
for(int i=0;i<a[u].size();i++){
int v=a[u][i].v;
if(v==fa)continue;
dfs(v,u);
size[u]+=size[v];
ans=(ans+2ll*size[v]*(n-size[v])%MOD*a[u][i].w%MOD)%MOD;
}
}
void work(){
ans=0;
for(int i=1;i<=n;i++)a[i].clear();
for(int i=1;i<n;i++){
int u=read(),v=read(),w=read();
a[u].push_back(qq{v,w});
a[v].push_back(qq{u,w});
}
dfs(1,0);
for(int i=1;i<n;i++)
ans=1ll*ans*i%MOD;
printf("%lld\n",ans);
}
int main(){
while(scanf("%d",&n)!=EOF)work();
}
J - YJJ’s Salesman (扫描线+线段树)
description
一个人从(0,0)到(1e9,1e9),每次只能往(x+1,y), (x,y+1) or (x+1,y+1)这三个方向走,当(x,y)有村庄i且从(x-1,y-1)走过去时,可以获得vi的价值,问最后的最大收获
solution
扫描线,并用线段树存储到当前(x,i)的最大收益。
code
#include<bits/stdc++.h>
using namespace std;
struct kk{
long long x,y,v;
};
struct kk2{
long long l,r,maxv;
}tree[500010];
long long pos[500010];
kk vig[500010],c[500010];
bool comp1(const kk&a,const kk&b){
return a.x<b.x;
}
bool comp2(const kk&a,const kk&b){
return a.y<b.y;
}
void build(long long l,long long r,long long p){
tree[p].l=l; tree[p].r=r; tree[p].maxv=0;
if(l==r){
pos[l]=p;
return;
}
build(l,(l+r)/2,p*2);
build((l+r)/2+1,r,p*2+1);
}
void add(kk x){
tree[pos[x.y]].maxv=max(tree[pos[x.y]].maxv,x.v);
long long i=pos[x.y]/2;
while(i!=0){
tree[i].maxv=max(tree[i*2].maxv,tree[i*2+1].maxv);
i=i/2;
}
}
long long find(long long l,long long r,long long p){
// cout<<l<<" "<<r<<" "<<p<<endl;
if(tree[p].l==l&&tree[p].r==r) return tree[p].maxv;
if(tree[p*2].r>=r) return find(l,r,p*2);
if(tree[p*2+1].l<=l) return find(l,r,p*2+1);
return max(find(l,tree[p*2].r,p*2),find(tree[p*2+1].l,r,p*2+1));
}
int main(){
long long t,n,num=0;
long long ans=0;
cin>>t;
while(t--){
cin>>n;
ans=0;
build(0,100000,1);
for(int i=1;i<=n;i++) scanf("%d%d%d",&vig[i].x,&vig[i].y,&vig[i].v);
sort(vig+1,vig+n+1,comp2);
long long tmp=0,last=0;
for(int i=1;i<=n;i++) {
int nlast=vig[i].y;
if(vig[i].y!=last) vig[i].y=++tmp;
else vig[i].y=tmp;
last=nlast;
}
sort(vig+1,vig+n+1,comp1);
num=0;
for(int i=1;i<=n;i++){
if(vig[i].x!=vig[i-1].x){
for(int j=1;j<=num;j++) add(c[j]);
num=0;
}
vig[i].v=find(0,vig[i].y-1,1)+vig[i].v;
c[++num]=vig[i];
ans=max(ans,vig[i].v);
}
cout<<ans<<endl;
}
return 0;
}