1. 程式人生 > >2018.12.15【NOIP提高組】模擬B組

2018.12.15【NOIP提高組】模擬B組

解題報告

JZOJ 100046 收集卡片

題目

詢問一段字母種類最多的最短區間


分析

掃描每一個結尾,找到最適合的開頭,統計最短長度


程式碼

#include <cstdio>
#include <cctype>
#define
rr register
using namespace std; char s[500001]; int n,cnt[151],sum=1,ans=2147473647,ans1=1,head=1; signed main(){ scanf("%d",&n); rr char c=getchar(); while (!isalpha(c)) c=getchar(); cnt[s[1]=c]=1; for (rr int i=2;i<=n;++i){ if (++cnt[s[i]=getchar()]==1) ++sum; while (head<i&&
cnt[s[head]]>1){ if (!(--cnt[s[head]])) --sum; ++head; } if (ans1<sum) ans1=sum,ans=i-head+1; else if (ans1==sum) ans=ans>(i-head+1)?(i-head+1):ans; } printf("%d",ans); return 0; }

JZOJ 100047 基因變異

題目

給定一個數字 x

x n n 個指定的數,可以在某一個二進位制位取反(也就是異或 2 t 2^t ),可以異或指定的數,問最少需要多少次操作才能變成 y y ,( T 100000 , n 20 T\leq 100000,n\leq 20


分析

一看詢問的數量就知道必須快速查詢,然後想了半天都沒想好,後來發現可以從0開始廣搜找對於每個數最少的次數,預處理後 O ( 1 ) O(1) 查詢。查詢的時候可以這樣想,若異或的數是 a n s ans ,那麼 x x xor a n s ans xor y y = x x xor y y xor a n s ans ,所以說 x x xor y y 即為需要查詢的數
時間複雜度 O ( 2 20 + T ) O(2^{20}+T)


程式碼

#include <cstdio>
#include <algorithm>
#include <cctype>
#include <queue>
#define rr register
using namespace std;
int a[21],cnt[1050001],n,t;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
signed main(){
	n=iut(); t=iut(); rr queue<int>q; q.push(0);
	for (rr int i=1;i<=n;++i) a[i]=iut();
	sort(a+1,a+1+n); n=unique(a+1,a+1+n)-a-1;//剪枝
	while (q.size()){
		int x=q.front(); q.pop();
		for (rr int i=1;i<=n;++i)
		if (!cnt[x^a[i]]&&x!=a[i])
		cnt[x^a[i]]=cnt[x]+1,q.push(x^a[i]);//指定的數
		for (rr int i=0;i<20;++i)
		if (!cnt[x^(1<<i)]&&x!=(1<<i))
		cnt[x^(1<<i)]=cnt[x]+1,q.push(x^(1<<i));//取反某一二進位制位
	}
	while (t--){
		rr int x=iut(),y=iut();
		print(cnt[x^y]); putchar(10);
	}
	return 0;
}

JZOJ 100044 abcd

題目

有4個長度為 n n 的陣列 a , b , c , d a,b,c,d ,現在需要選擇 n n 個數構成陣列 e e ,使 a i e i b i a_i\leq e_i\leq b_i ,且 i = 1 n e [ i ] × c [ i ] = 0 \sum_{i=1}^ne[i]\times c[i]=0 ,最後使 i = 1 n e [ i ] × d [ i ] \sum_{i=1}^ne[i]\times d[i] 最大


分析

這道題目可以轉換為選擇 a i a_i b i b_i 個物品,單個費用為 c i c_i ,價值為 d i d_i ,並使價值最大,說到這裡其實就是一個多重揹包,不過可以直接取 a i a_i 個,然後把 b i a i b_i-a_i 進行多重揹包,但是單純的多重揹包依然會超時,單調佇列又太麻煩,所以說可以用二進位制優化,把它轉換成01揹包


程式碼

#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
#define max(a,b) ((a)>(b))?(a):(b)
using namespace std;
int w[4001],v[4001],f[50001],n,m,ans;
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)&&c!='-') c=getchar();
	if (c=='-') f=-f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
signed main(){
	memset(f,0xcf,sizeof(f)); f[0]=0;
	for (rr int t=iut();t;--t){
		rr int a=iut(),b=iut(),c=iut(),d=iut();
		ans+=a*d,m-=a*c,b-=a;//容量在多重揹包的時候用費用補回去
		for (rr int i=1;i<=b;i<<=1)
			w[++n]=i*c,v[n]=i*d,b-=i;//二進位制拆分
		if (b) w[++n]=b*c,v[n]=b*d;	
	}
	for (rr int i=1;i<=n;++i)//01揹包
	    for (rr int j=m;j>=w[i];--j)
	        f[j]=max(f[j],f[j-w[i]]+v[i]);
	printf("%d",f[m]+ans);
	return 0;
}