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
題意:有一個很社保的老闆用打印機打印東西,要按照順序打印,必須在前一個打印機完成一份打印之後才能開始打印,求每天打印任務最少完成時間
做的時候大概想了想,用暴力二分,統計每個時間點能完成的任務數量,二分找,搞定。
正解就是上面那個方法加上優先隊列等等優化(……完全不是一個方法了喂!!!)
首先,要麼在所有機器都啓用之前完成該任務,要麼之後,
先用優先隊列把每個時間能完成的任務都記錄下來
然後二分時間
#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;
}