2019.08.18【NOIP提高組】模擬 B 組 貪心+離散+KMP

0 能量獲取

“封印大典啓動,請出Nescafe魂珠!”隨着聖主applepi一聲令下,聖劍護法rainbow和魔杖護法freda將Nescafe魂珠放置於封印臺上。封印臺是一個樹形的結構,魂珠放置的位置就是根節點(編號爲0)。還有n個其他節點(編號1-n)上放置着封印石,編號爲i的封印石需要從魂珠上獲取Ei的能量。能量只能沿着樹邊從魂珠傳向封印石,每條邊有一個能夠傳遞的能量上限Wi,魂珠的能量是無窮大的。作爲封印開始前的準備工作,請你求出最多能滿足多少顆封印臺的能量需求?

注意:能量可以經過一個節點,不滿足它的需求而傳向下一個節點。每條邊僅能傳遞一次能量。

對於100%的數據,滿足1<=n<=1000,0<=Fi<=n,0<=Ei,Wi<=100


貪心
正確性易證
每次選擇需求能量最小的點,判斷到根節點的邊是否都滿足能把該點需求滿足,更新答案,路過的邊的邊權都減一下
假的證明:
如果在該節點的子節點中有更划算的點,那麼更划算的點的能量需求一定是更小的(更划算就是指能滿足更多的點啊)

#include <cstdio>
#include <algorithm>

using namespace std;

int n,ans;
struct cv{
	int f,e,w,h;
}a[1003];
int s[1003];

bool comp(cv a,cv b){
	return a.e<b.e;
}

void read(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		scanf("%d%d%d",&a[i].f,&a[i].e,&a[i].w);
		a[i].h=i;
	}
	sort(a+1,a+1+n,comp);
	for (int i=1;i<=n;i++){
		s[a[i].h]=i;
	}
}

bool dfs(int x,int ww){
    if (x==0) return 1;
	if (a[x].w<ww) return 0;
	if (dfs(s[a[x].f],ww)) {a[x].w-=ww;return 1;}
	return 0;
}

void dp(){
	for (int i=1;i<=n;i++){
		if (dfs(s[a[i].h],a[i].e)) ans++;
	}
}

int main(){
	read();
	dp();
	printf("%d",ans);
}

1 封印一擊

“聖主applepi於公元2011年9月創造了Nescafe,它在散發了16吃光輝之後與公元2011年11月12日被封印爲一顆魂珠,貯藏於Nescafe神塔之中。公元2012年9月,聖主帶領四大護法重啓了Nescafe,如今已經是Nescafe之魂的第30吃傳播了。不久,它就要被第二次封印,而變成一座神杯。。。”applepi思索着Nescafe的歷史,準備着第二次封印。

Nescafe由n種元素組成(編號爲1~n),第i種元素有一個封印區[ai,bi]。當封印力度E小於ai時,該元素獲得ai的封印能量;當封印力度E在ai到bi之間時,該元素將獲得E的封印能量;而當封印力度E大於bi時,該元素將被破壞從而不能獲得任何封印能量。現在聖主applepi想選擇恰當的E,使得封印獲得的總能量儘可能高。爲了封印的最後一擊儘量完美,就請你寫個程序幫他計算一下吧!

對於50%的數據,1<=N<=1000,1<=ai<=bi<=10000。

對於100%的數據,1<=N<=105,1<=ai<=bi<=109。


可以看出選擇的E一定是在區間右端點的
假的證明:
如果已經選擇了一個E且E不在任意區間右端點上,那麼E可以移動到離它最近的區間右端點,這樣的移動對E原來取得的值不會有減少,因爲它沒有移出任何一個區間,原來沒選到的區間還是選不到;值會增加,因爲在區間內的取值是它自己

所以排序一下枚舉就好啦
話說這樣算是離散(?)

#include <cstdio>
#include <algorithm>

using namespace std;

const int N=100005;
int n,c;
long long ans;
long long a[N],b[N],s[N];

void read(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%lld%lld",&a[i],&b[i]);
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	long long k=1;
	for (int i=n;i>=1;i--) 
		s[i]=s[i+1]+a[i];
	for (int i=1;i<=n;i++){
		while (a[k]<=b[i]&&k<=n) k++;
		long long an=s[k]+(k-i)*b[i]; 
		if (an>ans) ans=an,c=b[i];
	}
}

int main(){
	read();
	printf("%d %lld",c,ans);
}

2 歸途與征程

在這裏插入圖片描述
對於30%的數據,M<=20;

對於80%的測試點,M<=200;

對於100%的測試點,1<=N<=100,1<=M<=100000。



可以根據*的位置把A分成幾個字符串,B的循環同構串就把它複製粘貼一遍,這樣每個長度爲B原長度m的子串都是它的循環同構串

題意就轉化爲在粘貼後的B中,有多少個長度爲m的子串,包含了A轉化出的所有子串

欸,一個字符串和多個字符串匹配呢,KMP搞一搞啦

KMP複習:
一個長的串B,判斷小的串A是否它的子串,並找出A出現的位置
next[i]:A自匹配,A中以i結尾的非前綴子串,與A前綴匹配長度
f[i]:B中以i開頭,與A的匹配長度 或 是否是A的一個匹配 或 其它什麼需要的數據

這題
設f[i][j] 表示B中以i開頭,是否匹配了A的第j個子串
g[i][j] 表示B中以i開頭,最先匹配A的第j個子串的位置
其中f[i][j]可以直接由KMP得
g[i][j] 由f 得
後面就枚舉B的循環同構串的開頭,看看是否能在m長度內把A的所有子串匹配完
其中A的開頭、結尾不是* 的需要特判,必須恰好和所枚舉的串頭或尾匹配
O(nm)O(nm)

#include <cstdio>

using namespace std;

char a[105],b[200006];
int n,m,ans,st,ed,l;
int c[105],g[200005][55];
int f[200005][55],next[106];

void kmp(char a[105],int len,int cd){
	for (int i=2,j=0;i<=n;i++){
		while (j>0&&a[j+1]!=a[i]) j=next[j];
		if (a[j+1]==a[i]) j++;
		next[i]=j;
	}
	for (int i=1,j=0;i<m;i++){
		while (j>0&&(j==n||a[j+1]!=b[i])) j=next[j];
		if (a[j+1]==b[i]) j++;
		if (j==len){
			f[i-len+1][cd]=1;
			j=next[j];
		}
	}
	g[m+1][cd]=m+1;
	for (int i=m;i>=1;i--){
		if (f[i][cd]==1) g[i][cd]=i;
		   else g[i][cd]=g[i+1][cd];
	}
}

void read(){
	char ch=getchar();
	while (ch!='*'&&(ch<'a')||ch>'z') ch=getchar();
	while (ch=='*'||(ch>='a'&&ch<='z')){
		a[++n]=ch;
		ch=getchar();
	}
	while (ch<'a'||ch>'z') ch=getchar();
	while (ch>='a'&&ch<='z'){
		b[++m]=ch;
		ch=getchar();
	}
	int cm=m;
	for (int i=1;i<=cm;i++){
		b[++m]=b[i];
	}
	if (a[1]=='*') st=1;
	if (a[n]=='*') ed=1;
	for (int i=1;i<=n;i++){
		while (a[i]=='*') i++;
		char s[105];
		int k=0;
		l++;
		while (a[i]!='*'&&i<=n){
			s[++k]=a[i];
			i++;
		}
		c[l]=k;
		kmp(s,k,l);
	}
}

int main(){
	read();
	for (int i=1;i<=m/2;i++){
		int x=i,nn=i+m/2,ss=1,ee=l;
		if (st!=1){
			if (f[i][1]==0)	continue;
			x=i+c[1];ss++;
		} 
		if (ed!=1){//&&ss<=l
			if (f[nn-c[l]][l]==0) continue;
			nn-=c[l];ee--;
		}
		for (int j=ss;j<=ee;j++){
			 x=g[x][j];
			 if (x>nn) break;
			 x+=c[j];
		}
		if (x<=nn||ss>ee) ans++;
	}
	printf("%d",ans);
}

榮耀永不散場

注意第三題的題目——歸途與征程
這個東西,放到百度搜索,翻到第二頁(或者第一頁,反正很前面),會有一個標題爲“【高喬】歸途與征程(1)-折戟沉沙 ” 的東西
你點開它,發現










它是一個全職同人文!!!!主角是喬一帆,第十七屆職業聯賽後退役,然後重生回第十區開區那天的故事
然後這還是一篇,純愛??!!!cp是高英傑x喬一帆??!

我,震驚
LOFTER的魔爪已經伸到
我不是不喜歡耽美,只是有點震驚於我搜個信競題目還會看到LOFTER
【高喬】歸途與征程(1)-折戟沉沙 感興趣的可以點開感受一下
我就不看了哈
哎我還是比較喜歡無cp的全職啦
雖然那麼一大幫年輕氣盛朝氣蓬勃的男孩子們聚在一起這個狀況非常誘人
我還是再堅持一下
如果我真香
那就再說吧
哎哎哎剛剛那個文只更了19章吶,寫到小喬和葉神一起下冰霜森林副本,感情線幾乎還沒展開

 、                    、
 、、                 、、
 、、、             、、、
 、、、、          、、、、
 ——————榮耀永不散場——————
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章