20181021模擬賽(暴力+暴力+優先隊列二分)

NOIP2016 提高組模擬賽

IzumiKonata
題目名Tetrix Tree Copier
輸入文件名tetrix.in tree.in copier.in
輸出文件名tetrix.out tree.out copier.out
時間限制1s 1s 1s
內存限制256M 256M 256M
測試點數量10 10 10
Linux 下評測。
1
1 Tetrix
1s; 256M
1.1 題目描述
俄羅斯方塊是一款歷史悠久的休閒小遊戲。
下面考慮一種和俄羅斯方塊無關的遊戲,我們將它稱爲Tetrix。
Tetrix 在一個大小爲N M 的方格中進行。玩家可以使用恰由4 個單位方格
組成的任意連通圖形不重疊地覆蓋這些方格。玩家得到的分數爲被覆蓋的方格數。
對於給定的N 和M,請求出最大可能的得分,並相應地給出一種覆蓋方案。
1.2 輸入格式
第一行一個正整數T 表示數據組數。
接下來T 行,每行包含2 個正整數N 和M。
1.3 輸出格式
對於每組測試數據:
第一行包含一個正整數,表示最大得分。
接下來N 行,每行M 個整數,描述一個矩陣A,即你的覆蓋方案。
1.3.1 覆蓋方案格式的約定
Ai;j 在32 位有符號整數範圍內。
Ai;j = 0 當且僅當方格(i; j) 沒有被覆蓋。
Ai1;j1 = Ai2;j2
̸= 0 當且僅當方格(i1; j1) 和(i2; j2) 被同一連通塊覆蓋。
1.4 樣例輸入
13
7
1.5 樣例輸出
20
2 2 3 3 3 5 5
1 2 2 3 0 5 5
1 1 1 4 4 4 4
1.6 數據規模
對於20% 的數據,1 N;M 5。
對於100% 的數據,1 N;M 32,1 T 5。
對於每個測試點,若你的最大得分均正確而方案至少有一個錯誤,可獲得30%
的部分分。
2
2 Tree
1s; 256M
2.1 題目描述
魔法森林裏有一棵魔法之樹,樹上的每一根枝幹都蘊藏着巨大的魔力。然而,
它最近的樣子有點異常。
經過一番調查後發現,這是一棵包含N 個節點的有根樹,根節點的編號爲1。
每條邊有一個魔力值vi,每個節點有一個容量值bi。如果對於一個節點i,存在它
的一個祖先j,滿足i 的容量值小於從i 到j 路徑上的魔力值之和,那麼節點i 處
就會發生魔力溢出。
爲了避免魔力溢出,我們希望剪去這棵樹的若干子樹(也可以不剪),使得剩
餘的樹中不存在魔力溢出的節點。爲了儘可能減少損失,請你求出剪去的節點個
數的最小值。
2.2 輸入格式
第一行一個正整數N,表示節點個數。
第二行N 個正整數b1; b2; :::; bn。
第三行N 􀀀 1 個正整數p2; p3; :::; pn,其中pi 表示節點i 的父親。
第四行N 􀀀 1 個整數v2; v3; :::; vn,其中vi 表示邊(pi; i) 的魔力值。
2.3 輸出格式
一行一個非負整數,表示剪去的節點個數的最小值。
2.4 樣例輸入
9
88 22 83 14 95 91 98 53 11
3 7 1 1 9 5 6 3
24 -8 67 64 65 12 -80 8
2.5 樣例輸出
5
2.6 樣例解釋
被刪除的節點編號爲2; 4; 6; 8; 9。
2.7 數據規模
對於30% 的數據,1 N 10。
對於60% 的數據,1 N 3000。
對於100% 的數據,1 N 100000; 1 pi < i; 1 bi 109; jvij 109。
3
3 Copier
1s; 256M
3.1 題目描述
K 終於下定決心擺脫NEET 生活,開始經營一家複印店。
K 有N 臺複印機。第i 臺複印機工作時,每ti 秒可以將一份文件複印一份。
多臺複印機可以同時工作,但每臺正在工作的複印機都必須佔用一份文件用於復
印。你可以在這些複印機停止工作後回收被佔用的文件。
K 每天只接待一位客戶(因爲懶)。通過使用某種與時間有關的能力,她知道
在接下來的M 天中,第i 天的客戶將要求把一份文件複印ai 份。
特別地,在同一天內,K 按編號逐個遞增的順序使用這些複印機。即對於任意
的i > 1,只有在編號小於i 的所有複印機都在工作時,K 纔會讓編號爲i 的複印
機開始工作。
現在K 希望知道,在接下來的M 天裏,她每天至少需要工作多少秒。
注意:由於K 擁有某種與時間有關的能力,你可以認爲一天的時長爲1018 秒,
即當天的請求一定會在當天完成。
3.2 輸入格式
第一行2 個正整數N 和M。
第二行N 個正整數ti。
第三行M 個正整數ai。
3.3 輸出格式
一行M 個正整數ansi,表示第i 天的最小工作時間。
3.4 樣例輸入
3 3
3 2 2
4 5 6
3.5 樣例輸出
7 7 9
3.6 樣例解釋
考慮ai = 5 的情況:
時刻0,把原件交給複印機#1,#1 開始工作。
時刻3,#1 完成了一份複印件。把這份複印件交給#2,#2 開始工作。
時刻5,#2 完成了一份複印件。把這份複印件交給#3,#3 開始工作。
時刻6,#1 完成了一份複印件。
時刻7,#2; 3 各完成了一份複印件。至此完成了5 份複印件。
4
3.7 數據規模

N M ti ai

0 5 5 5 100
1 50 50 100
2 100 100 N 􀀀 1
3 100
4 5000 N 􀀀 1
5 5000
6 1
7 N 􀀀 1
8
9
所有測試點均滿足:1 N 2 105; 1 M 103; 1 ti 103; 1 ai 109。

T1

題意:求用四個方格聯通塊能在不互相覆蓋最大覆蓋一個矩陣

直接蛇形排列,解決了。
(注意矩陣小於四個的時候稍微特判一下,要全輸出0)

#include<bits/stdc++.h>
using namespace std;
int a[40][40],n,m,cnt,p=0;
int main()
{
//	freopen("tetrix.in","r",stdin);
//	freopen("tetrix.out","w",stdout);
	int T;
	scanf("%d",&T);
	while (T--)
	  {
	  	scanf("%d%d",&n,&m);
	  	cnt=n*m%4;
	  	printf("%d\n",n*m-cnt);
	  	memset(a,0,sizeof(a));
        int i=1,j=1,color=1,numc=1,nowc=1,js=1;
		a[i][j]=color;
		while (numc<n*m-cnt)
		  {
		  	if (nowc==4) nowc=0,color++;
		  	j+=js;
		  	if (j>m) i++,js=-1,j=m;
		  	if (j<1) i++,js=1,j=1;
			a[i][j]=color; 
			numc++;
			nowc++;
	      }
	    for (int i=1; i<=n; i++)
        {
          for (int j=1; j<=m; j++)
            if (n*m-cnt==0) cout<<"0 "; else printf("%d ",a[i][j]);
          printf("\n");
        }
	  }
}

T2
題意:給你一棵樹,有點權有邊權,刪去一些點使之滿足每個點的權大於根到它的邊權和

暴力做吧,注意當加上下一條邊時候如果邊權和小於原先權值和時,此時邊權和清零。

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int head[maxn],n,m,cnt,fa[maxn],nsum[maxn];
long long a[maxn],ans; 
struct node
{
	int v,nxt;
	long long w;
}e[maxn<<1];
inline void add(int u,int v,int w)
{
	cnt++;
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
inline void build(int fath,int u)
{
	for (int i=head[u]; i; i=e[i].nxt)
	  {
	  	int v=e[i].v;
	  	//cout<<u<<' '<<v<<endl;
	  	if (v==fath) continue; 
		build(u,v);
	  	nsum[u]+=nsum[v];
	  }
	nsum[u]++;
}
inline void dfs(int fath,int u,long long num)
{
	for (int i=head[u]; i; i=e[i].nxt)
	{
		int v=e[i].v;
		if (v==fath) continue;
		if (max(num+e[i].w,e[i].w)>a[v]) 
		  {
		  	//cout<<u<<' '<<v<<' '<<num<<' '<<nsum[v]<<endl;
		  	ans+=nsum[v];
		  	continue;
		  }
		dfs(u,v,max(e[i].w,e[i].w+num));
 	}
}
int main()	
{
	scanf("%d",&n);
	for (int i=1; i<=n; i++) scanf("%d",&a[i]);
	for (int i=2; i<=n; i++) scanf("%d",&fa[i]);
	for (int i=2; i<=n; i++) 
	  {
	  	int x;
	    scanf("%d",&x);
	    add(i,fa[i],x);
	    add(fa[i],i,x);
	  }
	build(0,1);
	//for (int i=1; i<=n; i++)
	  //cout<<nsum[i]<<" ";
	dfs(0,1,0);
    printf("%lld",ans);
}

T3
題意:有一個很社保的老闆用打印機打印東西,要按照順序打印,必須在前一個打印機完成一份打印之後才能開始打印,求每天打印任務最少完成時間

做的時候大概想了想,用暴力二分,統計每個時間點能完成的任務數量,二分找a[i]a[i],搞定。

正解就是上面那個方法加上優先隊列等等優化(……完全不是一個方法了喂!!!)
首先,要麼在所有機器都啓用之前完成該任務,要麼之後,
先用優先隊列把每個時間能完成的任務都記錄下來
然後二分時間

#include<bits/stdc++.h>
#define ll long long
#define Max(x,y) (x>y ? x : y)
#define Min(x,y) (x<y ? x : y)
using namespace std;
template<typename tn> void read(tn &a){
	tn x=0,f=1; char c=' ';
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
	a=x*f;
}
const int N = 201000;
int n,m,t[N],p[N],h[1010],f[1010][1010];
ll s[1010],ans[1010];
struct node{
	int s,num;
}q[1010];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > qq;
bool cmp(node a,node b){return a.s<b.s;}
bool check(ll tim,int sum){
	ll sss=0;
	for(int i=1;i<=1000;i++){
		sss+=h[i]*(ll)(tim/i)-s[i]-f[i][tim%i+1];
	}
	return sss>=sum;
}
bool check2(ll tim,int sum){
	ll sss=0;
	for(int i=1;i<=n;i++) sss+=(tim-p[i])/t[i];
	return sss>=sum;
}
int main(){
	for(int i=1;i<=n;i++) read(t[i]);
	for(int i=1;i<=m;i++){
		read(q[i].s); q[i].num=i;
		} 
	int k=1,now=1;
	sort(q+1,q+m+1,cmp);
	qq.push(make_pair(t[1],1));
	if(n>1)
	while(!qq.empty()){
		int u=qq.top().second,tim=qq.top().first; qq.pop();
		p[++now]=tim;
		while(k<=m&&q[k].s<now)
			ans[q[k].num]=tim,k++;
		if(now==n) break;
		qq.push(make_pair(tim+t[u],u));
		qq.push(make_pair(tim+t[now],now));
	}
	while(!qq.empty()) qq.pop();
	for(int i=1;i<=n;i++)
		h[t[i]]++,f[t[i]][p[i]%t[i]]++,s[t[i]]+=p[i]/t[i];
	for(int i=1;i<=1000;i++)
		for(int j=i-1;j>=0;j--)
			f[i][j]+=f[i][j+1];
	ll lst=p[n];
	for(int i=k;i<=m;i++){
		ll l=lst-1,r=(ll)(1e12)/(ll)n+1000;
		if(n<=2500){
			while(l+1<r){
				ll mid=l+r>>1;
				if(check2(mid,q[i].s)) r=mid;
				else l=mid;
			}
		}
		else{
			while(l+1<r){
				ll mid=l+r>>1;
				if(check(mid,q[i].s)) r=mid;
				else l=mid;
			}
		}
		ans[q[i].num]=lst=r;
	}
	for(int i=1;i<=m;i++) cout<<ans[i]<<' ';
	return 0;
}

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