POI 2015 題解
在宋爺的教育下……我來自我教育一下,做一下近年的POI題……
論弱逼的自我修養
4385: [POI2015]Wilcze doły
給定一個長度爲n的序列,你有一次機會選中一段連續的長度不超過d的區間,將裏面所有數字全部修改爲0。
請找到最長的一段連續區間,使得該區間內所有數字之和不超過p。
顯然,選擇
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
const int N = 2000000+5;
using namespace std;
typedef long long LL;
LL q[N];
int l,r;
LL sum[N];
LL f[N],a[N];
inline LL read()
{
LL x = 0,f=1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f=-1;ch = getchar();}
while(ch >='0' && ch <='9'){x=(x<<1)+(x<<3)+ch-'0';ch = getchar();}
return x*f;
}
int main()
{
// freopen("ks.in","r",stdin);
// freopen("ks.out","w",stdout);
int n = read();
LL p = read();
int d = read();
for(int i=1;i<=n;++i) a[i] = read(), sum[i] = a[i] + sum[i-1];
for(int i=1;i<=n;++i) f[i] = sum[i+d-1] - sum[i-1];
int ans = d;
l = 1;
r = 1;
q[r] = 1;
LL sum = f[1];
for(int i=1,j=1;j<=n-d+1;)
{
while(sum - f[q[l]] <= p && j <= n-d+1){
j ++;
while(l <= r && f[q[r]] <= f[j] )--r;
q[++r] = j;
sum += a[j+d-1];
}
// cout << i << " " << j << endl;
ans = max(ans,j-i+d-1);
while(sum - f[q[l]] > p && i<=j){
while(l<=r && q[l]<=i) ++l;
sum -= a[i++];
}
}
cout << ans << endl;
}
4378[POI2015]Logistyka
維護一個長度爲n的序列,一開始都是0,支持以下兩種操作:
1.U k a 將序列中第k個數修改爲a。
2.Z c s 在這個序列上,每次選出c個正數,並將它們都減去1,詢問能否進行s次操作。
每次詢問獨立,即每次詢問不會對序列進行修改。
我們先考慮,加入如果一段區間大於等於
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#define lowbit(x) ((x)&(-x))
using namespace std;
const int N = 1000000;
typedef long long LL;
int T1[N+5];
LL T2[N+5];
struct ask
{
int chose;
int x,y;
}q[N+5];
void updata(int x,int num)
{
while(x <= N )
{
T1[x] += (num >= 0) ? 1 : -1;
T2[x] += (LL)num;
x += lowbit(x);
}
}
int ask1(int x)
{
int ans = 0;
while(x)
{
ans += T1[x];
x -= lowbit(x);
}
return ans;
}
LL ask2(int x)
{
LL ans = 0;
while(x)
{
ans += T2[x];
x -= lowbit(x);
}
return ans;
}
inline int read()
{
int x=0,f=1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f=-1;ch = getchar();}
while(ch >='0' && ch <='9'){x=(x<<1)+(x<<3)+ch-'0';ch = getchar();}
return x*f;
}
int a[N+5];
int V[N<<1];
int tt;
int find(int x)
{
int l = 1;
int r = tt;
while(l<=r)
{
int mid = (l+r)>>1;
if(V[mid] == x) return mid;
if(V[mid] < x) l = mid + 1;
else r = mid - 1;
}
return 0;
}
int main()
{
int n = read(), m = read();
char str[10];
for(int i=1;i<=m;++i)
{
scanf("%s",str);
q[i].chose = (str[0] == 'U');
q[i].x = read(),q[i].y = read();
V[++tt] = q[i].y;
}
V[++tt] = 0;
for(int i=1;i<=n;++i)
updata(1,0);
sort(V+1,V+tt+1);
for(int i=1;i<=m;++i)
{
if(q[i].chose == 1)
{
int tt = find(a[q[i].x]);
updata(tt,-a[q[i].x]);
a[q[i].x] = q[i].y;
tt = find(a[q[i].x]) ;
updata(tt,a[q[i].x]);
}
else
{
int tt = find(q[i].y);
int tt1 = ask1(N) - ask1(tt-1);
if(tt1 >= q[i].x){puts("TAK");continue;}
LL tt2 = ask2(tt-1);
if(tt2 >= ((LL)q[i].x - tt1)*q[i].y)
puts("TAK");
else puts("NIE");
}
}
}
3749: [POI2015]Łasuchy
圓桌上擺放着n份食物,圍成一圈,第i份食物所含熱量爲c[i]。
相鄰兩份食物之間坐着一個人,共有n個人。每個人有兩種選擇,吃自己左邊或者右邊的食物。如果兩個人選擇了同一份食物,這兩個人會平分這份食物,每人獲得一半的熱量。
假如某個人改變自己的選擇後(其他n-1個人的選擇不變),可以使自己獲得比原先更多的熱量,那麼這個人會不滿意。
請你給每個人指定應該吃哪一份食物,使得所有人都能夠滿意。
我們可以分析,對於一個人情況其實非常少,我們不妨嘗試第一個人的每一種情況,之後每一個人的合法情況就都能知道了,然後就是模擬了
這題作爲嘴巴選手AC還是非常容易的,但是真寫起來這是…………
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
const int N = 1000000+5;
using namespace std;
int n;
int a[N];
int path[N][5],ans[N];
inline int read()
{
int x=0,f=1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f=-1;ch = getchar();}
while(ch >='0' && ch <='9'){x=(x<<1)+(x<<3)+ch-'0';ch = getchar();}
return x*f;
}
bool work(int x)
{
memset(path,0,sizeof path);
path[1][x] = 1;
for(int i=2;i<=n;++i)
{
if(path[i-1][1] && (a[i-1] <= (a[i] << 1))) path[i][1] = 1;
if(path[i-1][4] && (a[i-1] <= a[i])) path[i][1] = 4;
if(path[i-1][2] && (a[i-1]<<1) >= a[i]) path[i][2] = 2;
if(path[i-1][3] && (a[i-1] >= a[i])) path[i][2] = 3;
if(path[i-1][1] && (a[i-1] <= a[i])) path[i][3] = 1;
if(path[i-1][4] && (a[i-1]<<1) <= a[i]) path[i][3] = 4;
if(path[i-1][2] && (a[i-1]>=a[i])) path[i][4] = 2;
if(path[i-1][3]&&(a[i-1]>=a[i]*2))path[i][4]=3;
}
if(path[n][x] == 0) return 0;
for(int i=n;i>=1;--i)
{
if(x == 1) ans[i-1] = (i-1) %(n-1) + 1;
if(x == 2) ans[i] = (i-1) %(n-1) + 1;
if(x == 3) ans[i-1] = ans[i] = (i-1)%(n-1) + 1;
x = path[i][x];
}
for(int i=1;i<n-1;++i) printf("%d ",ans[i]);
return printf("%d\n",ans[n-1]), 1;
}
int main()
{
n = read();
for(int i=1;i<=n;++i) a[i] = read();
a[++n] = a[1];
for(int i=1;i<=4;++i) if(work(i)) return 0;
puts("NIE");
return 0;
}
4383: [POI2015]Pustynia
給定一個長度爲n的正整數序列a,每個數都在1到10^9範圍內,告訴你其中s個數,並給出m條信息,每條信息包含三個數l,r,k以及接下來k個正整數,表示a[l],a[l+1],…,a[r-1],a[r]裏這k個數中的任意一個都比任意一個剩下的r-l+1-k個數大(嚴格大於,即沒有等號)。
請任意構造出一組滿足條件的方案,或者判斷無解。
首先,如果數字的個數比較少,我們可以這樣構建
1.如果存在一個數字
2.對圖進行拓撲排序,把起點的值設爲
3.如果有正環或者有點的範圍不在規定的範圍之內就
然而這題的點數太多了,耿直的建邊是
我們可以學習
這是一顆正常的線段樹
![](~/getGraph (1).jpg)
我們 把它改造,由父親指向兒子,邊權是0,表示沒有區間的限制關係
.jpg)
這時,假設有一號位置的點限制了後面三個點,我們可以這樣連邊
![](~/getGraph (3).jpg)
然後,就和正常的一樣了,拓撲排序判一個正環啥的就行了,由於邊最多有
#include <queue>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
const int N = 2000000;
const int M = 2000000;
const int inf = 1e9;
using namespace std;
int hash[N];
int tr[M];
int head[N];
char getc()
{
static const int LEN = 1<<15;
static char buf[LEN],*S=buf,*T=buf;
if(S == T)
{
T = (S=buf)+fread(buf,1,LEN,stdin);
if(S == T)return EOF;
}
return *S++;
}
int read()
{
static char ch;
static int D;
while(!isdigit(ch=getc()));
for(D=ch-'0'; isdigit(ch=getc());)
D=(D<<3)+(D<<1)+(ch-'0');
return D;
}
struct graph
{
int next,to,val;
graph () {}
graph (int _next,int _to,int _val)
:next(_next),to(_to),val(_val){}
}edge[M];
int in[M],MIN[M];
inline void add(int x,int y,int z)
{
static int cnt = 0;
edge[++cnt] = graph(head[x],y,z);
in[y] ++;
head[x] = cnt;
}
int cnt;
void build(int k,int l,int r)
{
tr[k] = ++cnt;
if(l==r){hash[l] = cnt;return;}
int mid = (l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
add(tr[k],tr[k<<1],0);
add(tr[k],tr[k<<1|1],0);
}
void ask(int k,int l,int r,int x,int y)
{
if(l==x && r==y){add(cnt,tr[k],1);return;}
int mid = (l+r)>>1;
if(y <= mid)ask(k<<1,l,mid,x,y);
else if(x > mid)ask(k<<1|1,mid+1,r,x,y);
else ask(k<<1,l,mid,x,mid),ask(k<<1|1,mid+1,r,mid+1,y);
}
queue <int> q;
int a[N];
int main()
{
register int i,j;
int n = read(),s = read(), m = read();
build(1,1,n);
for(i=1;i<=s;++i)
{
int tmp1 = read(),tmp2 = read();
a[tmp1] = tmp2;
MIN[hash[tmp1]] = tmp2;
}
for(i=1;i<=m;++i)
{
++cnt;
int l = read(),r = read(),k = read();
int lastL = l - 1,lastR = r + 1;
int pos;
for(j=1;j<=k;++j)
{
pos = read();
add(hash[pos],cnt,0);
if(pos - lastL > 1)
ask(1,1,n,lastL+1,pos-1);
lastL = pos;
}
if(lastR - lastL > 1)
ask(1,1,n,lastL + 1, lastR - 1);
}
for(i=1;i<=cnt;++i)
{
if(!MIN[i]) MIN[i] = inf;
if(!in[i]) q.push(i);
}
while(!q.empty())
{
int tt = q.front();q.pop();
for(i=head[tt];i;i=edge[i].next)
{
MIN[edge[i].to] = min(MIN[edge[i].to],MIN[tt] - edge[i].val);
if(!--in[edge[i].to])
q.push(edge[i].to);
}
}
bool flag = 0;
for(i=1;i<=cnt;++i)
if(in[i])
flag = 1;
for(i=1;i<=n;++i)
if(MIN[hash[i]] < 1)
flag = 1;
for(i=1;i<=n;++i)
if(a[i] > MIN[hash[i]])
flag = 1;
if(flag)
puts("NIE");
else
{
puts("TAK");
for(i=1;i<n;++i)
printf("%d ",MIN[hash[i]]);
cout << MIN[hash[n]] << endl;
}
}
4382: [POI2015]Podział naszyjnika
長度爲n的一串項鍊,每顆珠子是k種顏色之一。 第i顆與第i-1,i+1顆珠子相鄰,第n顆與第1顆也相鄰。
切兩刀,把項鍊斷成兩條鏈。要求每種顏色的珠子只能出現在其中一條鏈中。
求方案數量(保證至少存在一種),以及切成的兩段長度之差絕對值的最小值。
首先,考慮,如果只有兩種字符,我們只需要在遇到他的時候+1,再次遇到他的時候-1,然後前綴和相等的地方都是能取得。
現在變多了,我們可以這樣,還是維護一個
由於串非常多,所以我們不能只是單純的+1-1,我們可以利用
對於第一問,我們遍歷這個串,講與他顏色相同的所有位置扔到一個大隊列裏面,然後記錄一下一共有
對於第二問,直接雙指針掃一遍就行了。
複雜度都在
#include <map>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1100000+5;
const int seed = 2333333;
typedef unsigned long long ULL;
inline int read()
{
int x=0,f=1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f=-1;ch = getchar();}
while(ch >='0' && ch <='9'){x=(x<<1)+(x<<3)+ch-'0';ch = getchar();}
return x*f;
}
map <ULL,int>mp;
ULL col[N],val[N],cnt;
ULL sum[N];
int pre[N],next[N],q[N],a[N],n,k;
bool vis[N];
int calc(int x,int y)
{
int tt = y - x;
return abs(tt-(n-tt));
}
int main()
{
col[0] = 1;
n = read(), k = read();
for(int i=1;i<=n;++i)col[i] = col[i-1] * seed;
int tot = 0;
for(int i=1;i<=n;++i) a[i] = read();
for(int i=1;i<=n;++i)
{
if(pre[a[i]])
{
val[pre[a[i]]] += col[++tot];
val[i] -= col[tot];
}
pre[a[i]] = i;
}
for(int i=1;i<=n;++i) sum[i] = sum[i-1] + val[i] ;
for(int i=1;i<=n;++i)
{
if(mp[sum[i]])
next[mp[sum[i]]] = i;
mp[sum[i]] = i;
}
int ans = n;
int tail = 0;
for(int i=1;i<=n;++i)
if(!vis[i])
{
tail = 0;
for(int j=i;j;j=next[j])
vis[j] = 1,q[++tail] = j;
// puts("");
cnt += (ULL)(tail)*(tail-1)/2;
for(int i=1,j=1;i<=tail;i++)
{
while(j < i && calc(q[j],q[i]) > calc(q[j+1],q[i])) ++j;
ans = min(ans,calc(q[j],q[i]));
}
}
cout << cnt << " " << ans << endl;
}
4377: [POI2015]Kurs szybkiego czytania
給定n,a,b,p,其中n,a互質。定義一個長度爲n的01串c[0..n-1],其中c[i]==0當且僅當(ai+b) mod n < p。
給定一個長爲m的小01串,求出小串在大串中出現了幾次。
首先,
我們可以設短串的首個與長串的匹配位置爲
如果短串的第
否則不在
因爲串長
然後區間排個序,求個補集就行了
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
const int N = 1100000+5;
using namespace std;
int cnt = 0,n,m,A,b,p;
int ans = 0;
struct interval
{
int l,r;
interval () {}
interval (int _l,int _r):l(_l),r(_r){}
bool operator<(const interval &z)const
{
return l < z.l;
}
}a[N<<2];
void add(int l,int r){
if(l<=r)a[++cnt] = interval(l,r);
else a[++cnt] = interval(l,n-1),a[++cnt] = interval(0,r);
}
char s[N];
int main()
{
cin >> n >> A >> b >> p >> m ;
scanf("%s",s);
for(int i=0,now = 0;i<m;++i,(now+=A)%=n)
if(s[i] == '0')
add((p-now+n)%n,(n-1-now+n)%n);
else
add((n-now+n)%n,(p+n-now-1)%n);
for(int i=1,c=(b-A+n)%n;i<m;++i,c=(c-A+n)%n)
add(c,c);
sort(a+1,a+cnt+1);
int tmp = -1;
for(int i=1;i<=cnt;++i)
{
if(a[i].l > tmp) ans += a[i].l-tmp-1,tmp = a[i].r;
else tmp = max(tmp,a[i].r);
}
cout << ans + n - 1 - tmp;
}
4384: [POI2015]Trzy wieże
sb BZ ,卡我常數,毀我青春
但是題還是不錯的題
給定一個長度爲n的僅包含’B’、’C’、’S’三種字符的字符串,請找到最長的一段連續子串,使得這一段要麼只有一種字符,要麼有多種字符,但是沒有任意兩種字符出現次數相同
這題算是
這題我們可以也維護這三個東西,然後發現這其實是三維的東西,爭取固定兩維比較第三維
我們可以先把第一維排序,然後相同的第一維一起更新答案,再一起更新數據,處理了一維
第把第二維做成桶或者叫權值樹狀數組,維護差爲那個的時候座標的最大值,最小值
但是這樣還不夠,可能會出現第三維相同的情況,爲了規避這種情況,我們再維護一下最大值的第三維是什麼。
但是發現,如果衝突了也沒法解決,於是我們在維護一個次小值就行了,顯然這兩個的第二維不同
寫起來真是…………………………………………………………………………
#include <bits/stdc++.h>
using namespace std;
#define N 1100000
int n,lim,ans;
char s[N];
struct node
{
int a,b,c,pos;
node(){}
node(int a,int b,int c,int pos):a(a),b(b),c(c),pos(pos){}
friend bool operator < (const node &r1,const node &r2)
{return r1.a<r2.a;};
}p[N];
struct diw_tree
{
int mx[N<<1],sx[N<<1],cx[N<<1],mn[N<<1],sn[N<<1],cn[N<<1];
void init()
{
memset(mx,-0x3f,sizeof(mx));
memset(sx,-0x3f,sizeof(sx));
memset(mn,0x3f,sizeof(mn));
memset(sn,0x3f,sizeof(sn));
}
void insert(int x,int y,int v)
{
for(int i=x;i<=lim;i+=i&-i)
{
if(v>mx[i])
{
if(cx[i]!=y)sx[i]=mx[i];
mx[i]=v;cx[i]=y;
}
else if(y!=cx[i])
sx[i]=max(sx[i],v);
if(v<mn[i])
{
if(cn[i]!=y)sn[i]=mn[i];
mn[i]=v;cn[i]=y;
}
else if(y!=cn[i])
sn[i]=min(sn[i],v);
}
}
inline void query(int x,int y,int v)
{
for(int i=x;i;i-=i&-i)
{
if(cx[i]!=y)ans=max(ans,mx[i]-v);
else ans=max(ans,sx[i]-v);
if(cn[i]!=y)ans=max(ans,v-mn[i]);
else ans=max(ans,v-sn[i]);
}
}
}tr1,tr2;
int main()
{
scanf("%d",&n);lim=(n+1)<<1;
scanf("%s",s+1);
for(int i=1,j;i<=n;i++)
{
j=i;
while(s[j]==s[i]&&j<=n)j++;j--;
ans=max(ans,j-i+1);i=j;
}
for(int i=1,a=0,b=0,c=0;i<=n;i++)
{
if(s[i]=='B')a++;
if(s[i]=='C')b++;
if(s[i]=='S')c++;
p[i]=node(a-b,b-c,c-a,i);
}
tr1.init();tr2.init();
sort(p,p+1+n);
for(int i=0,j;i<=n;i++)
{
j=i;
while(p[j].a==p[i].a&&j<=n)j++;j--;
for(int k=i;k<=j;k++)
{
tr1.query(p[k].b-1+n+1,p[k].c,p[k].pos);
tr2.query(n-(p[k].b+1)+1,p[k].c,p[k].pos);
}
for(int k=i;k<=j;k++)
{
tr1.insert(p[k].b+n+1,p[k].c,p[k].pos);
tr2.insert(n-p[k].b+1,p[k].c,p[k].pos);
}
i=j;
}
printf("%d\n",ans);
return 0;
}
他告訴我,一些奇怪的特判……
Orz
4381: [POI2015]Odwiedziny
給定一棵
對於一次行走,假設起點爲x,終點爲y,步伐爲k,那麼Byteasar會從x開始,每步往前走k步,如果最後不足k步就能到達y,那麼他會一步走到y。
請幫助
調代碼的時候內心是崩潰的
好在開始寫的時候寫的不是很殘
首先,如果直接樹鏈剖分肯定是不行的[雖然這題確實能過
因爲極限情況其實會被卡成
不知道 沒算過 有算出更靠譜的可以下面留言
然後我們可以預處理一部分,正常的算法應該是像倍增預處理兩個東西
1.
2.
然後我們可以處理出
另外一種直接在樹剖上邊走邊統計就行了
這題真正的難點在於只能往上走,對於另外一邊的子樹,就會發生跨過
寫出來就是幾行,幾個字節GG就夠你調一陣的了
另外閾值其實不一定非得是
PS:經過多組測試,發現速度-閾值函數是一個單峯函數,在15左右取到速率的最大值
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
const int N = 50000+5;
const int M = N << 1;
const int lmt = 15;
using namespace std;
int head[N];
struct graph
{
int next,to;
graph () {}
graph (int _next,int _to)
:next(_next),to(_to){}
}edge[M];
inline void add(int x,int y)
{
static int cnt = 0;
edge[++cnt] = graph(head[x],y);
head[x] = cnt;
edge[++cnt] = graph(head[y],x);
head[y] = cnt;
}
int fa[N];
int PP[N][lmt+1];
int sum[N][lmt+1];
int depth[N],size[N],top[N];
int pos[N];
int belong[N];
int a[N];
void DFS1(int x)
{
size[x] = 1;
for(int i=2;i<=lmt;++i)
if(PP[fa[x]][i-1])
PP[x][i] = PP[fa[x]][i-1];
for(int i=1;i<=lmt;++i)
sum[x][i] = sum[PP[x][i]][i] + a[x];
for(int i=head[x];i;i=edge[i].next)
if(edge[i].to!=fa[x])
{
fa[edge[i].to] = x;
depth[edge[i].to] = depth[x] + 1;
PP[edge[i].to][1] = x;
DFS1(edge[i].to);
size[x] += size[edge[i].to];
}
}
void DFS2(int x,int chain)
{
static int cnt = 0;
belong[pos[x] = ++cnt] = x;
top[x] = chain;int k = 0;
for(int i=head[x];i;i=edge[i].next)
if(edge[i].to!=fa[x] && size[k] < size[edge[i].to])
k = edge[i].to;
if(!k) return; DFS2(k,chain);
for(int i=head[x];i;i=edge[i].next)
if(edge[i].to!=fa[x] && edge[i].to != k)
DFS2(edge[i].to,edge[i].to);
}
int Lca(int x,int y)
{
while(top[x] != top[y])
{
if(depth[top[x]] < depth[top[y]])swap(x,y);
x = fa[top[x]];
}
return depth[x] < depth[y] ? x : y;
}
int up(int x,int y)
{
while(depth[x] - depth[top[x]] < y)
{
y -= depth[x] - depth[top[x]] + 1;
x = fa[top[x]] ;
}
// cout << belong[pos[x]-y] <<endl;
return belong[pos[x] - y];
}
int Get(int x,int y,int v)
{
if(v < lmt) return sum[x][v] - sum[y][v] + a[y];
int ans = 0 , remain = 0 , t;
while(top[x]!=top[y])
{
for(t = pos[x] - remain ; t >= pos[top[x]]; t -= v)
ans += a[belong[t]];
t += v;
remain = v - ( t - pos[top[x]] + 1 );
x = fa[top[x]];
}
for(t = pos[x] - remain;t>=pos[y]; t-= v)
ans += a[belong[t]];
return ans;
}
int calc(int x,int y,int v)
{
int z = Lca(x,y);
int ans = 0;
int t = depth[x] - depth[z] - ( depth[x] - depth[z] )%v;
t = up(x,t);
ans = Get(x,t,v);
if(y == z)
return t == y ? ans : a[y];
int t1 = v - (depth[t] - depth[z]);
int t2 = depth[y] - depth[z] - t1;
if(t2 < 0) return ans + a[y];
int t3 = up(y,t2%v);
int t4 = up(y,t2);
ans += Get(t3,t4,v);
return t2%v ? ans + a[y] : ans;
}
inline int read()
{
int x=0,f=1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f=-1;ch = getchar();}
while(ch >='0' && ch <='9'){x=(x<<1)+(x<<3)+ch-'0';ch = getchar();}
return x * f;
}
int b[N];
int main()
{
int n = read();
for(int i=1;i<=n;++i) a[i] = read();
for(int i=1;i<n;++i)
{
int x = read(),y = read();
add(x,y);
}
DFS1(1);
DFS2(1,1);
/* for(int i=1;i<=n;++i)
{
for(int j=1;j<=3;++j)
cout << sum[i][j] << " ";
puts("");
}*/
for(int i=1;i<=n;++i) b[i] = read();
for(int i=1;i<n;++i){
int x = read();
printf("%d\n",calc(b[i],b[i+1],x));
}
}
4380: [POI2015]Myjnie
有n家洗車店從左往右排成一排,每家店都有一個正整數價格p[i]。
有m個人要來消費,第i個人會駛過第a[i]個開始一直到第b[i]個洗車店,且會選擇這些店中最便宜的一個進行一次消費。但是如果這個最便宜的價格大於c[i],那麼這個人就不洗車了。
請給每家店指定一個價格,使得所有人花的錢的總和最大。
首先,我們可以把他的價格離散,然後用
然後我們發現可以區間
爲了方便轉移,再維護一個後綴的最大值就行了
最後遞歸輸出一下
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
typedef long long LL;
using namespace std;
const int N = 50+2;
const int M = 4000+2;
int n,m,cnt;
int l[M],r[M],c[M],a[M];
int pos[N][N][M],val[N][N][M];
LL f[N][N][M],sum[N][N][M];
int V[M];
void print(int l,int r,int v)
{
if(r < l) return;
int tt = val[l][r][v];
print(l,pos[l][r][v]-1,tt);
printf("%d ",V[tt]);
print(pos[l][r][v]+1,r,tt);
}
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int stack[M];
int main()
{
cin >> n >> m;
register int k;
for(int i=1;i<=m;++i) l[i] = read(),r[i] = read(),V[i] = c[i] = read();
int top;
sort(V+1,V+m+1);
for(int i=1;i<=m;++i)
if(V[i]!=V[i-1])
V[++cnt] = V[i];
for(int i=0;i<n;++i)
for(int j=1;j+i<=n;++j)
{
int ri = j + i;
for(k=j;k<=ri;++k)
{
top = 0;
for(int t=1;t<=m;++t)
if(l[t] >= j && r[t] <= ri && l[t] <= k && r[t] >= k)
stack[++top] = c[t];
sort(stack+1,stack+top+1);
for(int t=1,now=1;t<=cnt;++t)
{
while(now <= top && stack[now] < V[t]) ++ now;
LL t1 = sum[j][k-1][t] + sum[k+1][ri][t] + (LL)V[t]*(top-now+1);
if(t1 >= f[j][ri][t])
{
f[j][ri][t] = t1;
pos[j][ri][t] = k;
}
}
}
for(k=cnt;k>=1;--k)
{
val[j][ri][k] = k;
if(f[j][ri][k] < sum[j][ri][k+1])
{
pos[j][ri][k] = pos[j][ri][k+1];
val[j][ri][k] = val[j][ri][k+1];
}
sum[j][ri][k] = max(sum[j][ri][k+1],f[j][ri][k]);
}
}
cout << sum[1][n][1] << endl;
print(1,n,1);
}
3747- [POI2015]Kinoman
共有m部電影,編號爲1~m,第i部電影的好看值爲w[i]。
在n天之中(從1~n編號)每天會放映一部電影,第i天放映的是第f[i]部。
你可以選擇l,r(1<=l<=r<=n),並觀看第l,l+1,…,r天內所有的電影。如果同一部電影你觀看多於一次,你會感到無聊,於是
無法獲得這部電影的好看值。所以你希望最大化觀看且僅觀看過一次的電影的好看值的總和。
我們可以枚舉左端點線段樹處理右端點
考慮到對
具體可以參見HH的項鍊一題
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#define N 1000000+5
#define M 4000000+5
typedef long long LL;
using namespace std;
LL tr[M],lazy[M];
inline void updata(int k)
{
tr[k] = max(tr[k<<1],tr[k<<1|1]);
}
inline LL read()
{
LL x=0,f=1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f=-1;ch = getchar();}
while(ch >='0' && ch <='9'){x=(x<<1)+(x<<3)+ch-'0';ch = getchar();}
return x*f;
}
inline void down(int k,int l,int r)
{
if(l==r || !lazy[k])return;
LL tmp = lazy[k];lazy[k] = 0;
tr[k<<1] += tmp;tr[k<<1|1] += tmp;
lazy[k<<1] += tmp;lazy[k<<1|1]+=tmp;
}
inline void change(int k,int l,int r,int x,int y,LL c)
{
down(k,l,r);
if(l==x && r==y){lazy[k] += c;tr[k] += c;return;}
int mid = (l+r)>>1;
if(y<=mid)change(k<<1,l,mid,x,y,c);
else if(x>mid)change(k<<1|1,mid+1,r,x,y,c);
else change(k<<1,l,mid,x,mid,c),change(k<<1|1,mid+1,r,mid+1,y,c);
updata(k);
}
LL a[N],b[N];
LL next[N],last[N];
int main()
{
int n = read(), m =read();
for(int i=1;i<=n;++i)
a[i] = read();
for(int i=1;i<=m;++i)
b[i] = read();
for(int i=n;i>=1;--i)
next[i] = last[a[i]],last[a[i]] = i;
for(int i=1;i<=m;++i)
if(last[i])
{
if(!next[last[i]])
change(1,1,n,last[i],n,b[i]);
else
change(1,1,n,last[i],next[last[i]]-1,b[i]);
}
LL ans = 0;
for(int i=1;i<=n;++i)
{
ans = max(ans,tr[1]);
int t = next[i];
if(t)
{
change(1,1,n,i,t-1,-b[a[i]]);
if(next[t])
change(1,1,n,t,next[t]-1,b[a[i]]);
else
change(1,1,n,t,n,b[a[i]]);
}
else
change(1,1,n,i,n,-b[a[i]]);
}
cout<<ans<<endl;
}
3750: [POI2015]Pieczęć
一張n*m的方格紙,有些格子需要印成黑色,剩下的格子需要保留白色。
你有一個a*b的印章,有些格子是凸起(會沾上墨水)的。你需要判斷能否用這個印章印出紙上的圖案。印的過程中需要滿足以下要求:
(1)印章不可以旋轉。
(2)不能把墨水印到紙外面。
(3)紙上的同一個格子不可以印多次。
mdzz
這是POI2015最水沒有之一,顯然現在途中最左上的點必然對應着一個印章的最左上
然後 思博模擬
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
const int N = 1000+5;
using namespace std;
struct data
{
int x,y;
data () {}
data (int _x,int _y)
:x(_x),y(_y){}
}p[N*N];
int cnt;
char s1[N][N],s2[N][N];
int main()
{
int T;
cin >> T;
int n,m,a,b;
while(T--)
{
cin >> n >> m >> a >> b;
for(int i=1;i<=n;++i)scanf("%s",s1[i]+1);
cnt = 0;
for(int i=1;i<=a;++i)
{
scanf("%s",s2[i]+1);
for(int j=1;j<=b;++j)
if(s2[i][j] == 'x')
p[++cnt] = data(i,j);
}
bool flag = false;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
if(s1[i][j] == 'x')
{
for(int k=1;k<=cnt;++k)
{
data t1 = data(i+p[k].x-p[1].x,j+p[k].y-p[1].y);
if(t1.x > n || t1.y > m )
{
flag = 1;
break;
}
if(s1[t1.x][t1.y] != 'x')
{
flag = 1;
break;
}
s1[t1.x][t1.y] = '.';
}
}
if(flag) break;
}
if(flag) break;
}
if(flag)
puts("NIE");
else puts("TAK");
}
}