第六次ACM訓練(Saturday)

總結

第六次訓練成績不太好,這是一場完全比拼手速地題目,這方面我不在行,想得比別人慢,敲代碼也比別人慢一點,輸了。

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+np=mp+np(m+n)^p=m^p+n^p(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,找出an+bn=cna^n+b^n=c^n的一組解

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(n1)di,j2*(n-1)\sum{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;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章