1. 程式人生 > 其它 >noip模擬賽34 Merchant Equation Rectangle

noip模擬賽34 Merchant Equation Rectangle

Merchant

考場思路:

考場上想到了二分,但是並不知道nth_element這種高階的東西,於是用了優先佇列。但是由於忘記了清空佇列,所以掛了。

正解:

由題意可知,每一個物品的集合必定是一個一次函式。隨著時間的增加,最大值是先降後增的。在下降區段,必定是沒有零點優的,所以只需要先特判零點,然後對於以後的時間進行二分,使用nth_element,O(N)求出最小的n-m個值,選出剩下的至多m個值,判斷是否符合條件,變更l,r。

tip:nth_element(a+1,a+m+1,a+n+1);能夠在O(n)時間內處理出最小的m個值(是無序的)。

程式碼實現
#include<bits/stdc++.h>
#define lt long long
#define int long long
using namespace std;

const int N=10000050;
int n,m;
lt s;
lt k[N],b[N],a[N];
lt ans,ens;
signed main(){
	scanf("%lld%lld%lld",&n,&m,&s);
	for(int i=1;i<=n;++i){
		scanf("%lld%lld",&k[i],&b[i]);
		a[i]=b[i];
	}
	sort(a+1,a+1+n);
	for(int i=n;i>=n-m+1;i--){
		ans+=a[i];
		if(ans>=s){
			printf("0\n");
			return 0;
		}
	}
	lt l=0,r=10000000005;
	while(1<r-l){
		lt mid=(r+l)>>1;
		for(int i=1;i<=n;++i){
			a[i]=k[i]*mid+b[i];
		}
		nth_element(a+1,a+n-m+1,a+n+1);
		bool b=0;
		ans=0;
		for(int i=n;i>=n-m+1;--i){
			if(a[i]>0)ans+=a[i];
			if(ans>=s){
				b=1;
				break;
			}
		}
		if(b){
			r=mid;
		}
		else{
			l=mid;
		}
//		printf("%lld %lld %lld\n",mid,ans,s);
	}
	cout<<r<<endl;
	return 0;
}

Equation

考場思路:

考場上想到了正解,然鵝由於失智行為,只得了3分(不刪freopen都能得這麼多分)

正解:

從根節點向下按照式子推,可以發現,每一個數都可以表示成 \(x_{i} =k+x_{1}\)\(x_{i} =k-x_{1}\) 的形式,此時對於詢問我們便可以輕鬆的回答詢問了。對於修改操作我們發現,深度奇偶性相同的兩個點,修改其中一個點,對於另一個的貢獻是相同的;而對於相反的點,修改其中一個點,對於另一個的貢獻是相反的。知道了這一點我們就可以建出線段樹(用樹狀陣列更快,線段樹要大力卡常)去維護修改值,詢問時對於奇數點與偶數點分別處理就好了。

程式碼實現
#include<bits/stdc++.h>
#define lt long long
using namespace std;

const int N=1000050;
int n,q;
int head[N],tot;
int dep[N],fa[N],id[N],rk[N],idnt,size[N];
lt t[N*4],val[N],yb[N];

struct edge{
	int nxt,to,dis;
}e[N*2];

inline int rd(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}


void ade(int x,int y,int z){
	e[++tot].nxt=head[x];
	e[tot].to=y;
	e[tot].dis=z;
	head[x]=tot;
}

void dfs1(int u){
	id[u]=++idnt;
	rk[idnt]=u;
	size[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		dep[v]=dep[u]+1;
		val[v]=e[i].dis-val[u];
		dfs1(v);
		size[u]+=size[v];
	}
}
inline void pushdown(int u){
	if(!t[u]) return;
	t[u<<1]+=t[u];
	t[u<<1|1]+=t[u];
	t[u]=0;
}

void edit(int u,int l,int r,int ll,int rr,lt x){
	if(l>rr||r<ll){
		return;
	}
	if(l>=ll&&r<=rr){
		t[u]+=x;
		return;
	}
	pushdown(u);
	register int mid=(l+r)>>1;
	edit(u<<1,l,mid,ll,rr,x);
	edit(u<<1|1,mid+1,r,ll,rr,x);
}

int ask(int u,int l,int r,int x){
	if(l==r){
		return t[u];
	}
	pushdown(u);
	register int mid=(l+r)>>1,ans;
	if(x<=mid){
		ans=ask(u<<1,l,mid,x);
	}
	else{
		ans=ask(u<<1|1,mid+1,r,x);
	}
	return ans;
}

void que(int x,int y,lt z){
	if((dep[x]&1)==(dep[y]&1)){
		if(!(dep[x]&1)){
			lt anx,any,cnx,cny,cx;
			anx=ask(1,1,n,id[x]);
			any=ask(1,1,n,id[y]);
			cnx=anx+val[x];
			cny=any+val[y];
			cx=cnx+cny-z;
			if(cx&1){
				printf("none\n");
				return;
			}
			else{
				cx/=2;
				printf("%lld\n",cx);
			}
		}
		else{
			lt cnx,cny,cx;
			cnx=ask(1,1,n,id[x]);
			cny=ask(1,1,n,id[y]);
			cnx=val[x]-cnx;
			cny=val[y]-cny;
			cx=z-cnx-cny;
			if(cx&1){
				printf("none\n");
				return;
			}
			else{
				cx/=2;
				printf("%lld\n",cx);
			}
		}
	}
	else{
		if(dep[x]&1){
			lt cnx,cny;
			cnx=ask(1,1,n,id[x]);
			cny=ask(1,1,n,id[y]);
			cnx=val[x]-cnx;
			cny+=val[y];
			if(cnx+cny==z){
				printf("inf\n");
			}
			else{
				printf("none\n");
			}
		}
		else{
			swap(x,y);
			lt cnx,cny;
			cnx=ask(1,1,n,id[x]);
			cny=ask(1,1,n,id[y]);
			cnx=val[x]-cnx;
			cny+=val[y];
//			printf("%d %d %d %d %d %d",x,y,anx,cnx,any,cny);
			if(cnx+cny==z){
				printf("inf\n");
			}
			else{
				printf("none\n");
			}
		}
	}
}

int main(){
//	freopen("shuju.in","r",stdin);
//	freopen("my.out","w",stdout);
	scanf("%d%d",&n,&q);
	int fc,wc;
	for(register int i=2;i<=n;i++){
		fc=rd(),wc=rd();
		fa[i]=fc;
		yb[i]=wc;
		ade(fc,i,wc);
	}
	dep[1]=1;
	dfs1(1);
	int tp,uc,vc;
	lt sc,we;
	for(register int i=1;i<=q;++i){
		tp=rd();
		if(tp==1){
			uc=rd(),vc=rd(),sc=rd();
			que(uc,vc,sc);
		}
		else{
			uc=rd(),we=rd();
			if(!(dep[uc]&1)){
				edit(1,1,n,id[uc],id[uc]+size[uc]-1,we-yb[uc]);
				yb[uc]=we;
			}
			else{
				edit(1,1,n,id[uc],id[uc]+size[uc]-1,yb[uc]-we);
				yb[uc]=we;
			}
		}
	}
	return 0;
}
## Rectangle ####考場思路: 考場沒思路……

正解:

很是難寫的一道題。
首先列舉矩形的右邊界,對於每一個有邊界,從右往左列舉左邊界

考慮左右邊界在A,B之間的矩形,都可以以A到B為長,對於在藍色區間的C點,和在橙色區間的D點,若兩者和A,B構成點集,則上邊界為C點,下邊界為D點,左邊界為B,右邊界為A。每這樣的兩對點都能對答案產生貢獻,於是得出以下式子
\(\displaystyle\sum_{i=1}^{mn}\displaystyle\sum_{j=mx}^{inf}(j-i)\)
將i從裡面提出來,定義size[A]為從零到A的節點數量,定義sum[A]為從零到A的節點的縱座標總和。
每一個C節點都有size[A]個貢獻,減去的D節點的和為sum[A]
\(\displaystyle\sum_{i=mx}^{inf}i*size[A]-sum[A]\)


再將i表示出來
\(sum[i]*size[j]-sum[j]*size[i]\)
如果再l與r上有多個點時

對於綠色部分顯然不能用B和A時維護,否則會有重複,所以在B和A時,只能維護藍色區間,要在C和A時,再去維護黃色區間。
對於sum和size使用樹狀陣列維護與查詢(每次移動右邊界時,清空樹狀陣列)

程式碼實現
#include<bits/stdc++.h>
#define lt long long
using namespace std;

const int N=10050,mod=1e9+7;
int n;
int x[N],y[N];
vector<int> mc[3000];
int xmx,ymx;
int size[3000],sum[3000];
bool vis[3000][3000];
lt ens,anx,any;

int low(int x){
	return x&-x;
}

void add(int x,int y){
	x++;
	for(int i=x;i<=ymx+1;i+=low(i)){
		size[i]+=1;
		sum[i]+=y;
	}
}

void ask(int x,int y){
	y++;
	lt ana=0,anb=0,bna=0,bnb=0;
	for(int i=x;i;i-=low(i)){
		ana+=size[i];
		anb+=sum[i];
	}
	for(int i=y;i;i-=low(i)){
		bna+=size[i];
		bnb+=sum[i];
	}
//	printf("[%d %d %d %d]\n",ana,anb,bna,bnb);
	anx=bna-ana;any=bnb-anb;
//	if(anx<0) anx=0;
//	if(any<0) any=0;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d%d",&x[i],&y[i]);
		xmx=max(xmx,x[i]);
		ymx=max(ymx,y[i]);
		mc[x[i]].push_back(y[i]);
	}
	for(int i=1;i<=xmx;++i){
		sort(mc[i].begin(),mc[i].end());
		mc[i].push_back(ymx+1);
	}
	for(int i=1;i<=xmx;++i){
//		cout<<endl<<i<<" rk "<<ens<<endl;
		memset(size,0,sizeof(size));
		memset(sum,0,sizeof(sum));
		
		int szi=mc[i].size()-1;
		if(!szi)continue;
		for(int k=0;k<szi;++k){
			if(!vis[i][mc[i][k]]){
				vis[i][mc[i][k]]=1;
				add(mc[i][k],mc[i][k]);
			}
		}
		for(int j=i-1;j>=1;--j){
			int szj=mc[j].size()-1;
			if(!szj) continue;
			for(int k=0;k<szj;++k){
				if(!vis[i][mc[j][k]]){
					vis[i][mc[j][k]]=1;
					add(mc[j][k],mc[j][k]);
				}
			}
			int tp1=0,tp2=0,up=max(mc[i][0],mc[j][0]);
			while(mc[i][tp1+1]<=up) tp1++;
			while(mc[j][tp2+1]<=up) tp2++;
			while(tp1<szi&&tp2<szj){
				int uup=min(mc[i][tp1+1],mc[j][tp2+1]);
				int down=min(mc[i][tp1],mc[j][tp2]);
				int aa,bb,cc,dd;
				ask(up,uup-1);
				aa=anx,bb=any;
				ask(1,down);
				cc=anx,dd=any;
				ens=(ens+((lt)(i-j)*(bb*cc-dd*aa)))%mod;
				up=uup;
				if(mc[i][tp1+1]<=up)tp1++;
				if(mc[j][tp2+1]<=up)tp2++;
//				printf("%d %d|",tp1,tp2);
			}
		}
	}
	printf("%lld\n",ens);
	return 0;
}