设 z = L C A ( x , y ) , l ≤ x , y , z ≤ r z=LCA(x,y),l\leq x,y,z\leq r z=LCA(x,y),l≤x,y,z≤r,当 y = z y=z y=z时, L C A ( x , y ) = z LCA(x,y)=z LCA(x,y)=z,所以这时候 x ∗ k = y x*k=y x∗k=y,应为 x ≠ y x\neq y x=y所以 k ≥ 2 k\geq2 k≥2, x x x最小取 l l l,y最小取 2 ∗ l 2*l 2∗l。
所以只要检验 2 ∗ l 2*l 2∗l是否 ≤ r \leq r ≤r就行了。
很明显因为不可以连续向左走,所以只可能是这样的走法:
线一直往前走,回头走一步,再往前,回头走一步……
假设再 i i i的位置回头,则回头一布再往前一步就相当于多得了 a i + a i − 1 a_i+a_{i-1} ai+ai−1分,贪心考虑一定是再最大的 a i + a i − 1 a_i+a_{i-1} ai+ai−1地方一直回头再往前,所以只需要枚举走到哪里,前面最大的 a i + a i − 1 a_i+a_{i-1} ai+ai−1是多少就行了。
Code:(珍爱生命远离抄袭)
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define int LL
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int n;
int a[100000+20];
signed main(){
fastio;
int t;
cin>>t;
while(t--){cin>>n;
int k,z;
cin>>k>>z;
rb(i,1,n) cin>>a[i];
LL res=a[1],sum=a[1],maxi=0;
rb(i,2,n){
sum+=a[i];
maxi=max((LL)a[i]+a[i-1],maxi);
LL ok=k-i+1;
if(ok<0) break;
if(ok>0)
ok=min((ok)/2,(LL)z);
else ok=0;
res=max(res,sum);
res=max(res,sum+ok*maxi);
LL tmp=sum,tmp2=0;
int rest=k-i+1;
rb(j,1,1000)
{
if(rest==0) break;
rest--;
if(j%2==1){
if(tmp2==z) break;
tmp2++;
tmp+=a[i-1];
}
else{
tmp+=a[i];
}
if(tmp2==z) break;
if(rest==0) break;
}
// cout<
res=max(res,tmp);
}
cout<<res<<endl;
}
return 0;
}
两次旋转转 t n 与 t 2 , t 3 与 t 1 , t 2 与 t 5 t_n与t_2,t_3与t_1,t_2与t_5 tn与t2,t3与t1,t2与t5一 一对应。
可以发现最终的串有两种符合条件的情况:
对于第一种,枚举剩下的是什么字符就行了,对于第二宗枚举奇数位的和偶数位的字符。
code:
/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int t;
int dp[2];
int main(){
fastio;
cin>>t;
while(t--){
string s;
cin>>s;
int n=s.length();
int best=0;
rb(i,0,9)
rb(j,0,9){
dp[0]=dp[1]=-INF;
dp[1]=0;
rep(k,n){
int l0,l1;
l0=dp[0];
l1=dp[1];
if(s[k]=='0'+i){
dp[0]=max(dp[0],l1+1);
}
if(s[k]=='0'+j){
dp[1]=max(dp[1],l0+1);
}
}
best=max(best,dp[1]);
}
int res=n-best;
int cnt[10]={0};
rep(i,n)
cnt[s[i]-'0']++;
rb(i,0,9)
res=min(res,n-cnt[i]);
cout<<res<<endl;
}
return 0;
}
首先如果初始状态不相交,则必须要使至少一对达到相交状态。使一对相交后,必然存在一段时间只要扩展任意一个一次,答案+1。以后必须要两个都扩展才可以加1。所以只需要枚举使多少对相交就行了。
code:
/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define int LL
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int n,k,l1,r1,l2,r2;
int check(int L1,int R1,int L2,int R2){
if(R1<L2){
return 0;
}
return min(R1,R2)-max(L1,L2);
}
void solve(){
R2(n,k);
R2(l1,r1);
R2(l2,r2);
if(l1>l2){
swap(l1,l2);
swap(r1,r2);
}
int reach=0;
if(r1<l2){
reach=l2-r1;
}
LL sa;
LL init=sa=check(l1,r1,l2,r2);
init*=n;
k-=init;
if(k<=0){
cout<<0<<endl;
return ;
}
int maxi=max(r1,r2)-min(l1,l2)-sa;
LL res=0;
LL tmp=1000000000000000;
rb(i,1,n){
res+=reach;
if(maxi>=k){
res+=k;
k=0;
break;
}
else{
res+=maxi;
k-=maxi;
tmp=min(tmp,res+2*k);
}
}
if(k>0){
cout<<tmp<<endl;
}
else
cout<<min(res,tmp)<<endl;
}
signed main(){
fastio;
int t;
cin>>t;
while(t--) {solve(); }
return 0;
}
根据题意可列出方程:
m x + y ≡ m y + x ( m o d w , 1 ≤ y , x ≤ m i n ( m , d ) ) m x − x ≡ m y − y ( m o d w , 1 ≤ y , x ≤ m i n ( m , d ) ) x ∗ ( m − 1 ) ≡ y ∗ ( m − 1 ) ( m o d w , 1 ≤ y , x ≤ m i n ( m , d ) ) ( y − x ) ∗ ( m − 1 ) ≡ 0 ( m o d W ) mx+y\equiv my+x(mod w,1\leq y,x\leq min(m,d))\\ mx-x\equiv my-y(mod w,1\leq y,x\leq min(m,d))\\ x*(m-1)\equiv y*(m-1) (mod w,1\leq y,x\leq min(m,d))\\ (y-x)*(m-1) \equiv 0 (mod W) mx+y≡my+x(modw,1≤y,x≤min(m,d))mx−x≡my−y(modw,1≤y,x≤min(m,d))x∗(m−1)≡y∗(m−1)(modw,1≤y,x≤min(m,d))(y−x)∗(m−1)≡0(modW)
设 z = l c m ( ( m − 1 ) % w , w ) / ( m − 1 ) % w z=lcm((m-1)\%w,w)/(m-1)\%w z=lcm((m−1)%w,w)/(m−1)%w,所以 ( y − x ) ≡ z ( m o d W ) (y-x)\equiv z(modW) (y−x)≡z(modW)
然后就可以算了。
code:
/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define int LL
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int t;
signed main(){
fastio;
cin>>t;
while(t--){
int m,d,w;
R2(m,d);
R(w);
if(d==1){
cout<<0<<endl;
continue;
}
int z=d-1;
z%=w;
if(z==0){
cout<<(LL)min(d,m)*(min(d,m)-1)/2<<endl;
continue;
}
int best=z*w/__gcd(z,w)/z;
if(best>=d||best>=m){
cout<<0<<endl;
continue;
}
int r=min(d,m);
LL res=0;
int fr=r%best;
int can=r/best;
if(fr)
res+=fr*can;
can--;
if(can>0)
res+=(can)*(can+1)/2*best;
cout<<res<<endl;
}
return 0;
}
考虑最后剩下的是什么样的情况:
大概是这样:
一段区间内只有颜色为1的线段,一段区间内只有颜色为2的线段,一段区间内只有颜色为1的线段……
这样就可以想到dp。*下文方便起见,颜色变为1,0
d p i , j − > 从 i 开 始 了 一 段 只 有 颜 色 j 的 区 间 dp_{i,j}->从i开始了一段只有颜色j的区间 dpi,j−>从i开始了一段只有颜色j的区间
d p i , j = m a x { d p k , j X o r 1 + f ( i , k − 1 , j ) } dp_{i,j}=max\{dp_{k,{j Xor 1}}+f(i,k-1,j)\} dpi,j=max{dpk,jXor1+f(i,k−1,j)}
f ( l , r , j ) − > 有 多 少 颜 色 为 j 的 线 段 再 区 间 [ l , r ] 内 f(l,r,j)->有多少颜色为j的线段再区间[l,r]内 f(l,r,j)−>有多少颜色为j的线段再区间[l,r]内
这样的转移时 O ( m a x { r i } 2 ) O(max\{r_i\}^2) O(max{ri}2),直接爆炸。
接下来想怎样优化。
很明显可以离散化,复杂度变为了 O ( n 2 ) O(n^2) O(n2),呃(⊙﹏⊙),还是过不了。
接下来我们考虑 d p i , j 与 d p i + 1 , j dp_{i,j}与dp_{i+1,j} dpi,j与dpi+1,j的转移有什么区别。对!考虑了左端点为i的颜色为j的线段的 贡献。可以用线段树来优化!
code:
/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
int n;
const int MAXN=2e5;
int delta[MAXN*8][2],tree[MAXN*8][2];
vector<pair<int,bool > > rsave[MAXN*2];
const int N=1<<19;
int add(int a,int b,bool flag,int now=1,int l=1,int r=N+1){
if(r<=a||l>=b){
return tree[now][flag]+delta[now][flag];
}
if(r<=b&&l>=a){
delta[now][flag]++;
// cout<
return tree[now][flag]+delta[now][flag];
}
int mid=(l+r)>>1;
return (tree[now][flag]=max(add(a,b,flag,now<<1,l,mid),add(a,b,flag,now<<1|1,mid,r)))+delta[now][flag];
}
int query(int a,int b,bool flag,int now=1,int l=1,int r=N+1){
// cout<
if(r<=a||l>=b) return 0;
if(r<=b&&l>=a){
return tree[now][flag]+delta[now][flag];
}
int mid=(l+r)>>1;
// cout<
return max(query(a,b,flag,now<<1,l,mid),query(a,b,flag,now<<1|1,mid,r))+delta[now][flag];
}
void push_down(int index,bool flag){
delta[index<<1][flag]+=delta[index][flag];
delta[index<<1|1][flag]+=delta[index][flag];
tree[index][flag]+=delta[index][flag];
delta[index][flag]=0;
}
void go(int a,bool flag,int now=1,int l=1,int r=N+1){
if(r<=a||l>a){
return;
}
if(l==r-1){
tree[now][flag]+=delta[now][flag];
delta[now][flag]=0;
return;
}
push_down(now,flag);
int mid=(l+r)>>1;
go(a,flag,now<<1,l,mid);
go(a,flag,now<<1|1,mid,r);
}
void change(int index,bool flag,int val){
index+=N-1;
tree[index][flag]=val;
index>>=1;
while(index){
tree[index][flag]=max(
tree[index<<1][flag]+delta[index<<1][flag],
tree[index<<1|1][flag]+delta[index<<1|1][flag]
);
index>>=1;
}
}
vector<mp> seg[2];
map<int,int> m;
int main(){
fastio;
R(n);
rb(i,1,n){
int l,R;
R2(l,R);
int co;
R(co);
co--;
seg[co].PB(II(l,R));
m[l]=m[R]=1;
}
int cnt=0;
for(map<int,int> :: IT ite=m.begin();ite!=m.end();ite++){
ite->SEC=++cnt;
}
for(auto it:seg[0]){
it.FIR=m[it.FIR];
it.SEC=m[it.SEC];
// cout<
rsave[it.FIR].PB(II(it.SEC,0));
}
for(auto it:seg[1]){
it.FIR=m[it.FIR];
it.SEC=m[it.SEC];
// cout<
rsave[it.FIR].PB(II(it.SEC,1));
}
rl(i,cnt,1){
for(auto it:rsave[i]){
if(it.SEC==1){
add(it.FIR+1,N+1,0);
}
else{
add(it.FIR+1,N+1,1);
}
}
go(i,0);
go(i,1);
change(i,0,query(i+1,N+1,1));
change(i,1,query(i+1,N+1,0));
}
cout<<max(query(1,cnt+1,0),query(1,cnt+1,1))<<endl;
return 0;
}
首先双联通分量缩点。因为双联通分量存在一种定向方式使得每一对点可以互相到达。可以参考这题。
然后缩完点的图是一棵树,可以dp来做。
d p [ i ] 表 示 i 是 s a t u r a t e d , 以 i 为 根 的 子 树 最 大 值 为 多 少 dp[i]表示i是saturated ,以i为根的子树最大值为多少 dp[i]表示i是saturated,以i为根的子树最大值为多少
换根dp就ok了。
/*
AuThOr Gwj
*/
#include
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define int LL
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
const int MAXN=3e5+2;
int n,m,k,c[MAXN],w[MAXN],cBcc[MAXN],depth[MAXN],low[MAXN],belong[MAXN],cntBcc;
bool sp[MAXN],spBcc[MAXN],vis[MAXN];
vector<mp> g[MAXN];
stack<int> sta;
void dfs(int now,int pre,int deep){
sta.push(now);
depth[now]=deep;
low[now]=deep;
vis[now]=1;
for(auto it:g[now]){
int to=it.FIR;
if(to!=pre){
if(vis[to]){
low[now]=min(low[now],depth[to]);
}
else{
dfs(to,now,deep+1);
low[now]=min(low[now],low[to]);
}
}
}
if(low[now]==deep){
cntBcc++;
while(1){
int index=sta.top();
sta.pop();
belong[index]=cntBcc;
if(index==now) break;
}
}
}
int cntSp[MAXN];
int cntHave[MAXN];
int total=0;
LL dp[MAXN];//以i为根的子树含有special node数量 |..#..| i号点为saturated的最优代价
LL totC[MAXN],sumC[MAXN];//Σc[j](j∈son(i),cntSp[j]=0)+cBcc[i]
void dfsInit(int now,int pre){
cntSp[now]=spBcc[now];
cntHave[now]=spBcc[now];
total+=spBcc[now];
sumC[now]=cBcc[now];
for(auto it:g[now])
{
if(it.FIR!=pre)
{
dfsInit(it.FIR,now);
sumC[now]+=sumC[it.FIR];
cntSp[now]+=cntSp[it.FIR];
if(cntSp[it.FIR]){
cntHave[now]++;
}
}
}
}
int onlySon[MAXN][2];
void dfsDp(int now,int pre){
onlySon[now][0]=onlySon[now][1]=-1;
totC[now]=cBcc[now];
for(auto it:g[now]){
if(it.FIR!=pre)
if(!cntSp[it.FIR]){
totC[now]+=sumC[it.FIR];
}
}
dp[now]=totC[now];
for(auto it:g[now]){
if(it.FIR!=pre)
if(cntSp[it.FIR]){
if(onlySon[now][0]==-1){
onlySon[now][0]=it.FIR;
}
else{
onlySon[now][1]=it.FIR;
}
dfsDp(it.FIR,now);
if(cntSp[it.FIR]==total){
dp[now]+=dp[it.FIR];
}
else
dp[now]+=max(0ll,dp[it.FIR]-it.SEC);
}
}
}
int res[MAXN];
struct DATA{
int cntSp,dp,totC,sumC;
};
map<mp,int> id;
pair<DATA,DATA> Copy(int i,int j){
DATA d1,d2;
d1.cntSp=cntSp[i];
d1.dp=dp[i];
d1.sumC=sumC[i];
d1.totC=totC[i];
d2.cntSp=cntSp[j];
d2.dp=dp[j];
d2.sumC=sumC[j];
d2.totC=totC[j];
return II(d1,d2);
}
void Paste(int i,int j,DATA d1,DATA d2){
cntSp[i]=d1.cntSp;
dp[i]=d1.dp;
sumC[i]=d1.sumC;
totC[i]=d1.totC;
cntSp[j]=d2.cntSp;
dp[j]=d2.dp;
sumC[j]=d2.sumC;
totC[j]=d2.totC;
}
void change_root(int now,int pre){
// cout<
res[now]=dp[now];
for(auto it2:g[now]){
int it=it2.FIR;
if(it!=pre){
mp saveSon=II(onlySon[now][0],onlySon[now][1]);
int saveHave1=cntHave[now],saveHave2=cntHave[it];
pair<DATA,DATA> save;
save=Copy(now,it);
if(!cntSp[it]){
totC[now]-=sumC[it];
}
if(cntSp[it]){
if(cntSp[it]!=total)
dp[now]-=max(0ll,dp[it]-it2.SEC);
else
dp[now]-=dp[it];
}
dp[now]-=totC[now];
dp[it]-=totC[it];
sumC[now]-=sumC[it];
sumC[it]+=sumC[now];
cntSp[now]-=cntSp[it];
cntSp[it]+=cntSp[now];
cntHave[it]+=(bool)(cntSp[now]);
if(!cntSp[now]){
totC[it]+=sumC[now];
}
dp[now]+=totC[now];
dp[it]+=totC[it];
if(cntSp[now]){
if(cntSp[now]!=total){
if(dp[now]-it2.SEC>0){
dp[it]+=dp[now]-it2.SEC;
}
}
else{
dp[it]+=dp[now];
}
}
change_root(it,now);
Paste(now,it,save.FIR,save.SEC);
cntHave[now]=saveHave1,cntHave[it]=saveHave2;
onlySon[now][0]=saveSon.FIR;
onlySon[now][1]=saveSon.SEC;
}
}
}
signed main(){
fastio;
cin>>n>>m>>k;
rb(i,1,k){
int vi;
cin>>vi;
sp[vi]=1;
}
rb(i,1,n) R(c[i]);
rb(i,1,m){
int wi;
R(wi);
w[i]=wi;
}
vector<mp> edges;
rb(i,1,m){
int u,v;
R2(u,v);
id[II(u,v)]=id[II(v,u)]=i;
edges.PB(II(u,v));
g[u].PB(II(v,w[i]));
g[v].PB(II(u,w[i]));
}
dfs(1,0,1);//
// cout<
// rb(i,1,n) cout<
// rb(i,1,n)
// belong[i]=i;
rb(i,1,n)
cBcc[belong[i]]+=c[i];
rb(i,1,n)
spBcc[belong[i]]|=sp[i];
rb(i,1,n)
g[i].clear();
rep(i,m){
int u,v;
u=edges[i].FIR;
v=edges[i].SEC;
v=belong[v];
u=belong[u];
if(u!=v){
g[u].PB(II(v,w[i+1]));
g[v].PB(II(u,w[i+1]));
}
}
dfsInit(1,0);
dfsDp(1,0);
change_root(1,0);
rb(i,1,n){
cout<<res[belong[i]]<<" ";
}cout<<endl;
return 0;
}