noip模擬29[簡單的板子題](雖然我不會)

fengwu2005發表於2021-08-03

\(noip模擬29\;solutions\)

這次考試給我最大的傷害,讓我意識到了差距

這場考試可以說是非常的簡單,就是簡單到,看兩眼,打個表就有結果了

但是呢?我考得非常的完蛋,只有30pts

noip模擬29[簡單的板子題](雖然我不會)

據說上一屆做這題隨便切;

考完之後一看,這第一題第三題都有人切了,我就非常的傷心,但是把題都改過來之後

還是很開心,明天一定要好好考,今天的狀態非常不好

明天提起精神A掉至少一道題!!!!

\(T1\; 最長不下降子序列\)

確實,乍一看這題還是挺簡單的,我說著我樹狀陣列打的非常的熟練,

就個這玩意不是小case??我就開始敲,突然發現,這個資料範圍好像炸掉了。。

一不做二不休,打完了覺得應該拿到30分,就交上去了

然而因為第一個數不符合規律我特判了一下,忘記統計它的答案了。。。。。。00000

第一題就這樣無奈的爆零了。。。

其實正解就是一個小dp,也就是我前面的演算法加了點其他的東西,

你會發現這個模數只有150,這樣根據抽屜原理就可以得到,這個序列必然有迴圈節,且長度小於模數

(我考場上也意識到了這個模數不對勁,可惜就是沒有找到規律,下次多想想)

我們發現當這個序列有了迴圈節,那我們就可以直接去統計每個迴圈節的貢獻了

本題要求的是不下降子序列,所以每個迴圈節都至少有1的貢獻,要是上升就不一定了

當這個序列出現了迴圈節,我們就可以將這個序列分為三部分,整塊的迴圈節和前後散的

那我們就可以分別去統計貢獻,注意這裡前面散塊的貢獻不能只統計這一小塊的

因為你不知道跨越了n個迴圈節之後還會出現什麼,所以這個長度要弄成\(\mathcal{O(T^2)}\)

後面的直接統計就好了,你會發現這時候中間剩下的迴圈節就直接加1就行了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
ll n,t,a,b,c,d;
ll zq,s[90005];
ll fro,beh,dp[90005];
ll ans,sum,beg;
ll pos[305];
struct SZSZ{
	ll tr[305];
	int lb(int x){return x&(-x);}
	void ins(int x,ll v){for(re i=x;i<=d+1;i+=lb(i))tr[i]=max(tr[i],v);}
	ll query(int x){ll ret=0;for(re i=x;i;i-=lb(i))ret=max(ret,tr[i]);return ret;}
	void clear(){memset(tr,0,sizeof(tr));}
}sz;
signed main(){
	scanf("%lld%lld%lld%lld%lld%lld",&n,&t,&a,&b,&c,&d);
	int now=1;s[1]=t;
	while(!pos[s[now]]){
		pos[s[now]]=now;now++;
		s[now]=(s[now-1]*s[now-1]*a+s[now-1]*b+c)%d;
		//cout<<s[now]<<" ";
	}
	//cout<<endl;
	zq=now-pos[s[now]];
	beg=now-zq-1;
	fro=min(now-zq-1+zq*zq,n);
	//cout<<n<<" "<<fro<<" "<<zq<<endl;
	for(re i=1;i<=fro;i++){
		if(!s[i])s[i]=(s[i-1]*s[i-1]*a+s[i-1]*b+c)%d;
		ll tmp=sz.query(s[i]+1)+1;
		sz.ins(s[i]+1,tmp);
		dp[i]=max(dp[i],tmp);
		ans=max(ans,tmp);
		//cout<<i<<endl;
		//cout<<s[i]<<" "<<ans<<endl;
	}
	for(re i=1;i<=zq;i++){
		dp[i+beg]=dp[i+beg+zq*(zq-1)];
	}
	//cout<<endl;
	//cout<<ans<<endl;
	if(fro==n){printf("%lld",ans);return 0;}
	sum=(n-fro)/zq;
	beh=n-fro-sum*zq;
	//cout<<sum<<" "<<beh<<endl;
	for(re i=1;i<=zq;i++){
		sz.clear();ll mx=0;
		for(re j=1;j<=beh;j++){
			if(s[i+beg]>s[j+beg])continue;
			ll tmp=sz.query(s[j+beg]+1)+1;
			sz.ins(s[j+beg]+1,tmp);
			mx=max(mx,tmp);
		}
		ans=max(ans,dp[i+beg]+mx+sum);
	}
	printf("%lld",ans);
}

\(T2\; 完全揹包問題\)

這個的話,我也是直接打了個最最最普通的揹包就溜走了

我其實根本就沒看出來是個dp,考完沈隊一講,好像是有點明白,這個題的dp是這樣的

\(f[k][i][j]\)表示處理玩前k個物品,用了i個大物品,得到的總體積模上最小的體積為j的最小體積

就是說我們維護不了每一個值,但是我們可以讓這個揹包的體積模 \(V_0\) (就是所有物品體積中最小的那個)

其實也不需要最小的,任何一個小於L的都可以,因為我們找到最小的話,就可以無限曾加,得到揹包的體積

那麼轉移就分為當前物品是大物品還是小物品就行了,dp好設計,

因為這個物品可以隨便加,所以我們直接先把狀態轉移到當前層,然後層內轉移

你發現這個如果是大物品的話,就可以直接迴圈轉移,小物品的話,就有後效性了,

所以這個時候就直接跑最短路就好了,也就是同餘最短路。。。

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const ll N=55;
const ll M=1e5+5;
const ll K=1e4+5;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll n,m,v[N],w[M],c,l;
ll f[N][40][K],mv=0,mn=inf;
ll s=20001;
struct DIJ{
	ll to[M*2],nxt[M*2],head[M],rp;
	ll val[M*2];
	bool vis[M];
	void clear(){
		memset(head,0,sizeof(head));rp=1;
		memset(vis,false,sizeof(vis));
	}
	void add_edg(ll x,ll y,ll z){
		to[++rp]=y;
		val[rp]=z;
		nxt[rp]=head[x];
		head[x]=rp;
	}
	struct node{
		ll di,id;
		node(){}
		node(ll x,ll y){
			di=x;id=y;
		}
		bool operator < (node x)const{
			return di>x.di;
		}
	};
	ll dis[M];
	priority_queue<node> q;
	void smw(){
		while(!q.empty())q.pop();
		q.push(node(0,s));dis[s]=0;
		while(!q.empty()){
			ll now=q.top().id;
			ll dit=q.top().di;q.pop();
			if(vis[now])continue;
			vis[now]=true;dis[now]=dit;
			for(re i=head[now];i;i=nxt[i]){
				ll y=to[i];
				if(vis[y])continue;
				q.push(node(dit+val[i],y));
			}
		}
	}
}dj;
signed main(){
	scanf("%lld%lld",&n,&m);
	for(re i=1;i<=n;i++){
		scanf("%lld",&v[i]);
		mn=min(mn,v[i]);
		mv=max(mv,v[i]);
	}
	scanf("%lld%lld",&l,&c);
	memset(f,0x3f,sizeof(f));
	//if(f[0][0][0]>1e18)cout<<"sb"<<endl;
	f[0][0][0]=0;
	for(re k=1;k<=n;k++){
		if(v[k]>=l){
			for(re j=0;j<mn;j++)
				f[k][0][j]=f[k-1][0][j];
			for(re i=1;i<=c;i++)
				for(re j=0;j<mn;j++)
					f[k][i][j]=min(f[k-1][i][j],f[k][i-1][(1ll*j+mn*100-v[k]%mn)%mn]+v[k]);
		}
		else{
			for(re i=0;i<=c;i++){
				dj.clear();
				for(re j=0;j<mn;j++){
					dj.add_edg(s,j,f[k-1][i][j]);
					dj.add_edg(j,((1ll*j+v[k]%mn)%mn),v[k]);
				}
				dj.smw();
				for(re j=0;j<mn;j++)f[k][i][j]=dj.dis[j];
			}
		}
	}
	//cout<<0x3f3f3f3f<<endl;
	for(re i=1;i<=m;i++){
		scanf("%lld",&w[i]);
		bool flag=false;
		ll tmp=w[i]%mn;
		for(re j=0;j<=c;j++){
			if(w[i]>=f[n][j][tmp]){
				flag=true;break;
			}
			//cout<<f[n][j][w[i]%v[0]]<<endl;
		}
		//if(w[i]>=f[n][c][w[i]%v[0]])flag=true;
		if(flag)printf("Yes\n");
		else printf("No\n");
	}
}

\(T3\;最近公共祖先\)

這個題是考場上最水的一道題了

直接將每個點的答案轉化成每個黑點的貢獻,直接dfs序一下,然後更新過的就不要更新了

考完試10min就切掉了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
int n,m,w[N];
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
	to[++rp]=y;
	nxt[rp]=head[x];
	head[x]=rp;
}
bool vis[N];
int fa[N],dep[N];
int siz[N],son[N],top[N];
int dfn[N],dfm[N],idf[N],cnt;
void dfs1(int x){
	siz[x]=1;son[x]=0;
	dfn[x]=++cnt;
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa[x])continue;
		fa[y]=x;dep[y]=dep[x]+1;
		dfs1(y);
	}
	dfm[x]=cnt;
}
struct XDS{
	#define ls x<<1
	#define rs x<<1|1
	int tr[N*4],laz[N*4];
	void build(int x,int l,int r){
		tr[x]=-1;
		if(l==r)return ;
		int mid=l+r>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
	}
	void pushdown(int x){
		if(!laz[x])return ;
		laz[ls]=max(laz[ls],laz[x]);
		tr[ls]=laz[ls];
		laz[rs]=max(laz[rs],laz[x]);
		tr[rs]=laz[rs];
		laz[x]=0;
	}
	void update(int x,int l,int r,int ql,int qr,int v){
		if(ql>qr)return ;
		if(ql<=l&&r<=qr){
			laz[x]=max(laz[x],v);
			tr[x]=laz[x];
			return ;
		}
		pushdown(x);
		int mid=l+r>>1;
		if(ql<=mid)update(ls,l,mid,ql,qr,v);
		if(qr>mid)update(rs,mid+1,r,ql,qr,v);
		return ;
	}
	int query(int x,int l,int r,int pos){
		if(l==r)return tr[x];
		pushdown(x);
		int mid=l+r>>1;
		if(pos<=mid)return query(ls,l,mid,pos);
		else return query(rs,mid+1,r,pos);
	}
	#undef ls
	#undef rs
}xds;
signed main(){
	scanf("%d%d",&n,&m);
	for(re i=1;i<=n;i++)scanf("%d",&w[i]);
	for(re i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);
		add_edg(x,y);add_edg(y,x);
	}
	xds.build(1,1,n);
	dfs1(1);vis[1]=true;
	for(re i=1;i<=m;i++){
		char ch[10];int x;
		scanf("%s%d",ch,&x);
		if(ch[0]=='M'){
			int u=x;
			xds.update(1,1,n,dfn[u],dfm[u],w[u]);
			while(!vis[u]){
				vis[u]=true;
				xds.update(1,1,n,dfn[fa[u]],dfn[u]-1,w[fa[u]]);
				xds.update(1,1,n,dfm[u]+1,dfm[fa[u]],w[fa[u]]);
				u=fa[u];
			}
		}
		else{
			printf("%d\n",xds.query(1,1,n,dfn[x]));
		}
	}
}