題解
一道典型的線段樹勢能分析題目
我們先來思考一下:一次修改之後,如果要直接在線段樹每個節點上維護出最小值該怎麼做
再思考一下什麼情況不能在O(1)完成對最小值的修改
我們發現,區間與和區間或的操作本質就是拆位之後,對每一位分別做區間覆蓋操作
比如某一位上&0,就代表着一段區間要賦爲0
某一位上|1,就代表這一段區間要賦爲1
而&1、|0操作對序列沒有任何影響
於是我們把一次操作的有效位k提取出來
在線段樹的節點上,我們維護兩個值d0、d1,分別表示這段區間中哪些位全爲0,哪些位全爲1(狀壓爲二進制存儲)
如果k被d0、d1的並集包含,那麼我們就可以O(1)修改當前節點,並且打一個下傳的標記
O(1)修改分類討論一下就可以了,最後可以合併兩種情況
這裏pushdown的作用就是讓兒子節點與父親節點的信息保持一致,也可以做到O(1)
那爲什麼這樣做的複雜度是對的呢?
我們拆位來考慮,在最後的時間複雜度乘上一個O(k)即可
那麼如果當前區間需要向下走,當且僅當這一個區間不是全0或全1
但是我覆蓋之後,這段區間,就一定會變成全0或者全1
而一次覆蓋兩端的殘餘塊最多會增加logn個需要向下走的區間(即logn的勢能)
所以一位的時間複雜度爲O(logn)
總的時間複雜度爲O(nklogn)
代碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
void gi(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
#define N 500005
#define lc i<<1
#define rc i<<1|1
#define LOG 30
const int INF=2147483647;
int val[N];
struct node{
int l,r,mi,d[2];
bool la;
}a[N<<2];
inline void pushup(int i)
{
a[i].d[0]=a[lc].d[0]&a[rc].d[0];
a[i].d[1]=a[lc].d[1]&a[rc].d[1];
a[i].mi=min(a[lc].mi,a[rc].mi);
}
inline void cal(int i,int k,int op)
{
a[i].la=1;
int tmp=a[i].d[op^1]&k;
a[i].d[op^1]^=tmp;
a[i].d[op]^=tmp;
a[i].mi^=tmp;
}
inline void pushdown(int i)
{
if(a[i].l<a[i].r&&a[i].la){
cal(lc,a[i].d[0],0);
cal(rc,a[i].d[0],0);
cal(lc,a[i].d[1],1);
cal(rc,a[i].d[1],1);
a[i].la=0;
}
}
void build(int i,int l,int r)
{
a[i].l=l;a[i].r=r;
if(l==r){
a[i].mi=val[l];int x=val[l];
for(int j=0;j<=LOG;j++)
a[i].d[(x>>j)&1]|=(1<<j);
return;
}
int mid=(l+r)>>1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(i);
}
void insert(int i,int l,int r,int k,int op)
{
if(l<=a[i].l&&a[i].r<=r&&((a[i].d[0]^a[i].d[1])&k)==k){
cal(i,k,op);
return;
}
pushdown(i);
if(l<=a[lc].r)insert(lc,l,r,k,op);
if(r>=a[rc].l)insert(rc,l,r,k,op);
pushup(i);
}
inline int query(int i,int l,int r)
{
if(l<=a[i].l&&a[i].r<=r)return a[i].mi;
pushdown(i);
int ret=INF;
if(l<=a[lc].r)ret=min(query(lc,l,r),ret);
if(r>=a[rc].l)ret=min(query(rc,l,r),ret);
return ret;
}
void write(int x){if(x>=10)write(x/10);putchar(x%10+48);}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int n,m,i,l,r,x,op;
gi(n);gi(m);
for(i=1;i<=n;i++)gi(val[i]);
build(1,1,n);
while(m--){
gi(op);gi(l);gi(r);
if(op==3){
write(query(1,l,r));
putchar('\n');
}
else{
gi(x);if(op==1)x^=INF;
insert(1,l,r,x,op-1);
}
}
}
題解
一開始去想DP了
結果考試結束之後才發現自己想假了
因爲它的前驅有重複的點,所以就可能會重複計算某個點的代價
這樣例迷惑性好強啊,給的是一棵樹。。。這種做法竟然還過了樣例。。。
還是來看正解吧
首先有幾個概念
原函數:c^T*x
原不等式:Ax>=b、-x>=-t
原變量:x
原函數中變量的係數:c^T
原不等式中變量的係數:A、-1
原不等式的參數:b、-t
聽Freopen說,對偶問題就是
最小化 變 最大化
一個 原不等式 看成一個 新變量:(Ax>=b -----> y) ( -x>=-t ----->z)
然後最大化這些新變量的值
這些 新函數中變量的係數 就是 原不等式的參數的轉置
b^T、-t^T (注意,題解中的式子寫錯了,應該是b^T*y - t^T*z)
對每一個原變量寫出一個新的不等式
這些 新不等式的參數 就是 原函數中變量的係數的轉置(大於小於符號反向)
?????<=c
這些 新不等式中變量的係數 就是 原不等式中變量的係數的轉置
A^T*y - z <=c
注意,x在不同的不等式中對應的係數,寫在新不等式中也會對應不同的變量
終於搞完了。。。
然後就是一個最大費用循環流問題???
額,我沒看出來。。。
寫代碼還是會的
代碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define N 205
#define M 10005
#define LL long long
int fir[N],cur[N],to[M],nxt[M],cnt;
LL cap[M],cst[M];
void adde(int a,int b,LL c,LL d)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cap[cnt]=c;cst[cnt]=d;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cap[cnt]=0;cst[cnt]=-d;
}
int S,T,SS,TT,sz;
LL deg[N];
LL mic,flow,pc;
LL dis[N];bool vis[N];
queue<int> q;bool inq[N];
const LL INF=0x3f3f3f3f3f3f3f3fll;
bool spfa()
{
for(int i=1;i<=sz;i++)dis[i]=INF;
q.push(TT);inq[TT]=1;dis[TT]=0;
while(!q.empty()){
int u=q.front();q.pop();inq[u]=0;
for(int v,p=fir[u];p;p=nxt[p]){
v=to[p];LL w=cst[p^1];
if(cap[p^1]>0&&dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!inq[v])q.push(v),inq[v]=1;
}
}
}
return dis[SS]!=INF;
}
LL sap(int u,LL aug)
{
if(u==TT){mic+=aug*dis[SS];return aug;}
int tmp,ret=0;
vis[u]=1;
for(int v,&p=cur[u];p;p=nxt[p]){
v=to[p];
if(!vis[v]&&cap[p]>0&&dis[u]==dis[v]+cst[p]){
tmp=sap(v,min(aug,1ll*cap[p]));
cap[p]-=tmp;aug-=tmp;
cap[p^1]+=tmp;ret+=tmp;
if(aug==0)break;
}
}
vis[u]=0;
return ret;
}
void micflow()
{
flow=mic=0;
while(spfa()){
for(int i=1;i<=sz;i++)cur[i]=fir[i];
flow+=sap(SS,INF);
}
}
int t[N],c[N];
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
cnt=1;
int n,m,W,i,u,v;
scanf("%d%d%d",&n,&m,&W);
for(i=1;i<=n;i++)scanf("%d",&t[i]);
for(i=1;i<=n;i++)scanf("%d",&c[i]);
S=2*n+1;T=S+1;SS=T+1;TT=SS+1;sz=TT;
for(i=1;i<=m;i++){
scanf("%d%d",&u,&v);
adde(u+n,v,INF,0);
}
for(i=1;i<=n;i++){
pc+=1ll*t[i]*c[i];
deg[i+n]+=c[i],deg[i]-=c[i];
adde(i+n,i,c[i],t[i]);
adde(S,i,INF,0);
adde(i,i+n,INF,0);
adde(i+n,T,INF,0);
}
for(i=1;i<=sz;i++){
if(deg[i]>0)adde(SS,i,deg[i],0);
else adde(i,TT,-deg[i],0);
}
adde(T,S,INF,0);
int l=0,r=55005,mid;
while(l<r){
mid=(l+r)>>1;
for(i=2;i<=cnt;i+=2)
cap[i]+=cap[i^1],cap[i^1]=0;
cst[cnt-1]=mid;cst[cnt]=-mid;
micflow();
if(pc-mic>W)l=mid+1;
else r=mid;
}
printf("%d\n",l);
}
T3是烷烴計數
看懂了,但是並不會牛頓迭代,也不會分治NTT
這裏的Polya定理是任意置換所有子樹,所以一共有6種置換
這個p-q+s=1的本質還是邊點容斥,P(x)中由於是算的點等價類個數,不能把0的情況算進去
代碼:無