省选模拟赛
又双叒开始记这个东西了,最好别又记一半不计了。
省选模拟4
15+4+60
A.我醉
这个
考虑分讨串的奇偶性并二分答案,对于当前待验证的长度
然后卡常啊很烦注意到不刻意构造的情况下答案很小所以答案上界削一下就能过了要不然跟暴力坐一桌。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define MAXN 100005
#define ull unsigned long long
#define pii pair<ull,int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
const ull p=13331;
ull pw[MAXN],has1[MAXN],has2[MAXN];
int n;
set<pii>sav,stac;
struct node{
int v,w,nxt;
}edge[MAXN<<1];
int h[MAXN],tmp;
inline void add(int u,int v,int w){
edge[++tmp]=(node){v,w,h[u]};
h[u]=tmp;
}
int ans=1;
bool vis[MAXN],F;
int lim,typ,L;
int siz[MAXN],dp[MAXN],root,sum;
inline void getrt(int u,int fa){
if(F)return;
dp[u]=0;
siz[u]=1;
for(int i=h[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(vis[v]||v==fa)continue;
getrt(v,u);
siz[u]+=siz[v];
dp[u]=max(dp[u],siz[v]);
}
dp[u]=max(dp[u],sum-siz[u]);
if(dp[u]<dp[root])root=u;
}
inline void getdis(int u,int fa,int dep){
if(dep<=lim){
stac.insert(mp(has1[dep],dep));
pii tar=mp(has1[dep],L-dep);
if(*(sav.lower_bound(tar))==tar){
F=1;
return ;
}
}
else{
if(L&1){
ull Hpp=has1[dep-lim]*pw[dep-lim-1],Hps=has2[2*(dep-lim)-1]-has2[dep-lim-1];
if(Hpp==Hps){
stac.insert(mp(has1[dep]-has1[2*(dep-lim)-1]*pw[2*lim-dep+1],dep));
pii tar=mp(has1[dep]-has1[2*(dep-lim)-1]*pw[2*lim-dep+1],L-dep);
if(*(sav.lower_bound(tar))==tar){
F=1;
return ;
}
}
}
else{
ull Hpp=has1[dep-lim]*pw[dep-lim],Hps=has2[2*(dep-lim)]-has2[dep-lim];
if(Hpp==Hps){
stac.insert(mp(has1[dep]-has1[2*(dep-lim)]*pw[2*lim-dep],dep));
pii tar=mp(has1[dep]-has1[2*(dep-lim)]*pw[2*lim-dep],L-dep);
if(*(sav.lower_bound(tar))==tar){
F=1;
return ;
}
}
}
}
for(int i=h[u];i;i=edge[i].nxt){
int v=edge[i].v,w=edge[i].w;
if(v==fa||vis[v])continue;
has1[dep+1]=has1[dep]*p+w;
has2[dep+1]=has2[dep]+w*pw[dep];
getdis(v,u,dep+1);
}
}
inline void work(int u){
if(F)return ;
sav.clear();
sav.insert(mp(0,0));
for(int i=h[u];i;i=edge[i].nxt){
int v=edge[i].v,w=edge[i].w;
if(vis[v])continue;
has1[1]=has2[1]=w;
stac.clear();
getdis(v,u,1);
for(set<pii>::iterator it=stac.begin();it!=stac.end();it++)sav.insert(*it);
}
}
inline void solve(int u){
if(F)return ;
work(u);
vis[u]=1;
for(int i=h[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(vis[v])continue;
sum=siz[v],root=0,dp[root]=n;
getrt(v,u);
solve(root);
}
}
inline bool check(int tar){
memset(vis,0,sizeof(vis));
F=0;lim=tar/2;
root=0,dp[root]=sum=n;
L=tar;
getrt(1,0);
solve(root);
return F;
}
signed main(){
freopen("name.in","r",stdin);
freopen("name.out","w",stdout);
pw[0]=1;
for(int i=1;i<MAXN;i++)pw[i]=pw[i-1]*p;
scanf("%d",&n);
for(int i=1,u,v,w;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
int l=1,r=1650;
while(r>=l){
int mid=l+r>>1;
if(check(mid*2))ans=mid*2,l=mid+1;
else r=mid-1;
}
l=1,r=1800;
while(r>=l){
int mid=l+r>>1;
if(check(mid*2-1))ans=max(ans,mid*2-1),l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}
B.梧桐依旧
转化题意为求
把 Burnside 引理转化一下
其中
好玩的是考场上观察出来答案总包含这么个形式,然后弃了。
然后求一下轨道即等价类个数,钦定秩的个数为
然后这个是
C.卿且去
白送60pts还是比较友善的。
60pts也要用的一个结论:显然取
然后容易得到答案式子可以表示为
,后面那一坨也可以 min25。
#include<bits/stdc++.h>
#define int long long
#define MAXN 200005
#define idx(x) ((x)<=V?id[0][(x)]:id[1][n/(x)])
using namespace std;
const int mod=998244353,inv2=499122177;
int n;
bool vis[MAXN];
int prime[MAXN],tot;
int id[2][MAXN],V,w[MAXN],cnt;
int g[MAXN];
inline void INIT(int n){
vis[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i])prime[++tot]=i;
for(int j=1;j<=tot&&i*prime[j]<=n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
inline int qpow(int base,int power){
int res=1;
while(power){
if(power&1)res=res*base%mod;
base=base*base%mod;
power>>=1;
}
return res%mod;
}
inline int S(int x,int i){
if(x<2||prime[i]>=x)return 0;
int res=(g[idx(x)]-i*inv2%mod+mod)%mod;
for(int j=i+1;j<=tot&&prime[j]*prime[j]<=x;j++){
for(int k=1,p=prime[j];p<=x;p*=prime[j],k++){
res=(res+(S(x/p,j)+(k>1?1:0))*inv2%mod+mod)%mod;
}
}
return res%mod;
}
int Pi,val,ans;
signed main(){
freopen("yyds.in","r",stdin);
freopen("yyds.out","w",stdout);
scanf("%lld",&n);
V=sqrt(n);
for(int l=1,r=0;l<=n;l=r+1){
r=n/(n/l);
w[++cnt]=n/l;
if(w[cnt]<=V)id[0][w[cnt]]=cnt;
else id[1][n/w[cnt]]=cnt;
}
INIT(V);
for(int i=1;i<=cnt;i++)g[i]=(w[i]-1)%mod;
for(int j=1;j<=tot;j++){
for(int i=1;i<=cnt&&prime[j]*prime[j]<=w[i];i++)
g[i]=(g[i]-g[idx(w[i]/prime[j])]+j-1+mod)%mod;
}
Pi=g[1];
for(int i=1;i<=cnt;i++)g[i]=g[i]*inv2%mod;
val=(S(n,0)-S(n>>1,0)+mod)%mod;
ans=qpow(2,Pi)*((n-(n>>1))-val+mod)%mod;
printf("%lld\n",ans);
return 0;
}
省选模拟5
10+30+100
A.蛋糕
然蛋题面。
考虑转化方案为这样的构造:对一条直线横向地连到它相邻的点上,然后顺时针或者逆时针旋转且不切到其他点,这一部分答案应为
后面一部分就莫反一下,推出来之后可以给前面的汇总成
这三个长得很筛子,前两个分别分配一个
的形式,就可以做了。
B.巧克力
送30还是比较香的。
30pts可以用维护lst跑,先转成tj里的nxt然后答案式子是
不可持久化的话,就拿set维护一下nxt的修改,对于答案计算就用线段树维护全区间答案和最小的next以及它所属的下标,信息合并可以线段树二分一个左边最靠右的比右边min小的点然后合成新的右边的min,重算一下答案,单次复杂度
然后加一下可持久化,相当于是把原先存nxt的set和其他数组换成主席树,时间复杂度不变,空间卡一卡。
话是这么说的但是实现的细节挺恶心的,先摆个码。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define ll long long
#define MAXN 100005
using namespace std;
int n,q;
int a[MAXN],loc[MAXN],nxt[MAXN];
namespace Segment_Tree_ValIndex{
#define ls(p) tree[p].lson
#define rs(p) tree[p].rson
#define Ls(p) Tree[p].lson
#define Rs(p) Tree[p].rson
struct TREE{
int lson,rson;
int val;
}tree[(MAXN<<2)*18],Tree[(MAXN<<2)*18];
int rt[MAXN],tot;
inline void modify(int l,int r,int x,int k,int &p,int pre){
p=++tot;
tree[p]=tree[pre];
tree[p].val+=k;
if(l==r)return ;
int mid=l+r>>1;
if(x<=mid)modify(l,mid,x,k,ls(p),ls(pre));
else modify(mid+1,r,x,k,rs(p),rs(pre));
}
inline int lgetloc(int l,int r,int x,int p){
if(!tree[p].val)return 0;
if(l==r)return l;
int mid=l+r>>1;
int res=0;
if(x<=mid)return lgetloc(l,mid,x,ls(p));
res=lgetloc(mid+1,r,x,rs(p));
if(res)return res;
return lgetloc(l,mid,mid,ls(p));
}
inline int rgetloc(int l,int r,int x,int p){
if(!tree[p].val)return n+1;
if(l==r)return l;
int mid=l+r>>1;
int res=0;
if(x>mid)return rgetloc(mid+1,r,x,rs(p));
res=rgetloc(l,mid,x,ls(p));
if(res!=n+1)return res;
return rgetloc(mid+1,r,mid+1,rs(p));
}
int cnt,Rt[MAXN<<1];
inline void build(int l,int r,int &p){
p=++cnt;
if(l==r){
Tree[p].val=rt[l];
return;
}
int mid=l+r>>1;
build(l,mid,Ls(p));
build(mid+1,r,Rs(p));
}
inline void Modify(int l,int r,int x,int k,int &p,int pre){
p=++cnt;
Tree[p]=Tree[pre];
if(l==r){
Tree[p].val=k;
return ;
}
int mid=l+r>>1;
if(x<=mid)Modify(l,mid,x,k,Ls(p),Ls(pre));
else Modify(mid+1,r,x,k,Rs(p),Rs(pre));
}
inline int query(int l,int r,int x,int p){
if(l==r)return Tree[p].val;
int mid=l+r>>1;
if(x<=mid)return query(l,mid,x,Ls(p));
else return query(mid+1,r,x,Rs(p));
}
#undef ls(p)
#undef rs(p)
#undef Ls(p)
#undef Rs(p)
}
using Segment_Tree_ValIndex::Rt;
using Segment_Tree_ValIndex::rt;
using Segment_Tree_ValIndex::cnt;
using Segment_Tree_ValIndex::tot;
struct Segment_Tree{
#define ls(p) tree[p].lson
#define rs(p) tree[p].rson
struct TREE{
int lson,rson;
ll val,sum;
int nval,loc;
}tree[(MAXN<<2)*18];
int tot,rt[MAXN<<1];
inline int ask(int l,int r,int x,ll &y,int p){
if(l==r)return l;
int mid=l+r>>1;
if(tree[rs(p)].nval<x)return ask(mid+1,r,x,y,rs(p));
y+=tree[rs(p)].sum+tree[p].val;
return ask(l,mid,x,y,ls(p));
}
inline void push_up(int l,int r,int ls,int rs,int p){
if(tree[rs].nval<=tree[ls].nval){
tree[p].nval=tree[rs].nval;
tree[p].loc=tree[rs].loc;
tree[p].sum=tree[rs].sum;
return ;
}
tree[p].val=0;
tree[p].sum=tree[ls].sum+tree[rs].sum+(tree[p].val=(ll)tree[rs].nval*(tree[rs].loc-ask(l,r,tree[rs].nval,tree[p].val,ls))-tree[p].val);
tree[p].nval=tree[ls].nval;
tree[p].loc=tree[ls].loc;
}
inline void build(int l,int r,int &p){
p=++tot;
if(l==r){
tree[p].val=a[l];
tree[p].nval=nxt[l];
tree[p].loc=l;
return ;
}
int mid=l+r>>1;
build(l,mid,ls(p));
build(mid+1,r,rs(p));
push_up(l,mid,ls(p),rs(p),p);
}
inline void modify(int l,int r,int x,int d,int y,int &p,int pre){
p=++tot;
ls(p)=ls(pre),rs(p)=rs(pre);
if(l==r){
tree[p].nval=d;
tree[p].loc=l;
tree[p].val=y;
return ;
}
int mid=l+r>>1;
if(x<=mid)modify(l,mid,x,d,y,ls(p),ls(pre));
else modify(mid+1,r,x,d,y,rs(p),rs(pre));
push_up(l,mid,ls(p),rs(p),p);
}
inline int query(int l,int r,int x,int p){
if(l==r)return p;
int mid=l+r>>1;
if(x<=mid)return query(l,mid,x,ls(p));
else return query(mid+1,r,x,rs(p));
}
inline void change(int l,int r,int ul,int ur,int p){
if(l>=ul&&r<=ur){
push_up(l,r,p,tot+1,tot+1);
return ;
}
int mid=l+r>>1;
if(ur>mid)change(mid+1,r,ul,ur,rs(p));
if(ul<=mid)change(l,mid,ul,ur,ls(p));
}
}ST;
#define loc(p) ST.tree[p].loc
#define nval(p) ST.tree[p].nval
#define sum(p) ST.tree[p].sum
#define val(p) ST.tree[p].val
int T;
ll lans;
signed main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),loc[i]=n+1;
for(int i=n;i>=1;i--){
nxt[i]=loc[a[i]];
loc[a[i]]=i;
Segment_Tree_ValIndex::modify(1,n,i,1,rt[a[i]],rt[a[i]]);
}
ST.build(1,n,ST.rt[0]);
Segment_Tree_ValIndex::build(1,n,Rt[0]);
scanf("%d",&q);
for(int i=1,opt,x,l,r;i<=q;i++){
scanf("%d%d%d%d",&opt,&x,&l,&r);
l=(l+lans)%n+1;
r=(r+lans)%n+1;
if(opt==1){
++T;
ST.rt[T]=ST.rt[x];
Rt[T]=Rt[x];
int loc=ST.query(1,n,l,ST.rt[T]);
int p=Segment_Tree_ValIndex::query(1,n,val(loc),Rt[T]);
if(val(loc)==r)continue;
Segment_Tree_ValIndex::modify(1,n,l,-1,p,p);
Segment_Tree_ValIndex::Modify(1,n,val(loc),p,Rt[T],Rt[T]);
p=Segment_Tree_ValIndex::lgetloc(1,n,l,p);
if(p)ST.modify(1,n,p,nval(loc),val(loc),ST.rt[T],ST.rt[T]);
p=Segment_Tree_ValIndex::query(1,n,r,Rt[x]);
ST.modify(1,n,l,Segment_Tree_ValIndex::rgetloc(1,n,l,p),r,ST.rt[T],ST.rt[T]);
int tar=Segment_Tree_ValIndex::lgetloc(1,n,l,p);
if(tar)ST.modify(1,n,tar,l,r,ST.rt[T],ST.rt[T]);
Segment_Tree_ValIndex::modify(1,n,l,1,p,p);
Segment_Tree_ValIndex::Modify(1,n,r,p,Rt[T],Rt[T]);
}
else{
nval(ST.tot+1)=r+1;
sum(ST.tot+1)=0;
loc(ST.tot+1)=r;
ST.change(1,n,l,r,ST.rt[x]);
printf("%lld\n",lans=sum(ST.tot+1)+(ll)nval(ST.tot+1)*(loc(ST.tot+1)-l+1)-(ll)(l+r)*(r-l+1)/2);
}
}
return 0;
}
C.奶酪
喜欢简单题放T3。
抽一条直径出来。以一个直径端点为根dp子树直径,求答案的时候分讨一下。如果是非直径边肯定有一个答案就是直径,另一个可以dp。如果是直径边那有一个答案可以dp,另一个答案就把根换到另一个直径端点再dp一遍求出。
省选模拟6
100+10+25
A.鸬鹚
考虑把矩形按上端点排序,逐个插入即可实现一个踢队尾的均摊效果,然而合并是不太容易的,用线段树维护
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define MAXN 100005
#define M 2000005
using namespace std;
int n,top;
namespace MYYY_Fast_IO{
inline int read(){
int w{1},x{};
char c=getchar();
while(c<'0'||c>'9'){if(c == '-')w=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=getchar();
return w * x;
}
inline void write(int x){
if(x<0) x=-x,putchar('-');
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline void writeln(int x){write(x);putchar(10);}
inline void writek(int x){write(x);putchar(' ');}
}
using namespace MYYY_Fast_IO;
struct node{
int x1,x2,y1,y2;
bool operator<(const node &pre)const{
if(x1!=pre.x1)return x1<pre.x1;
if(x2!=pre.x2)return x2<pre.x2;
if(y1!=pre.y1)return y1<pre.y1;
return y2<pre.y2;
}
}sq[MAXN],ans[MAXN];
bool del[MAXN];
int hp[M];
int refl[M],tot,lfer[M],cnt;
inline bool cmp(node pre,node b){
return pre.y2<b.y2;
}
inline bool check(int x,int y){
node pre=sq[x],b=sq[y];
return pre.y2>b.y1&&pre.y1<b.y2&&pre.x2>b.x1&&pre.x1<b.x2;
}
inline void merge(int x,int y){
sq[x].x1=min(sq[x].x1,sq[y].x1);
sq[x].y1=min(sq[x].y1,sq[y].y1);
sq[x].x2=max(sq[x].x2,sq[y].x2);
sq[x].y2=max(sq[x].y2,sq[y].y2);
}
struct Segment_Tree{
#define ls(p) p<<1
#define rs(p) p<<1|1
#define sav(p) tree[p].sav
#define stac(p) tree[p].stac
struct TREE{
vector<int>sav,stac;
}tree[MAXN<<3];
inline void modify(int l,int r,int ul,int ur,int id,int p){
stac(p).emplace_back(id);
if(l>=ul&&r<=ur){
sav(p).emplace_back(id);
return;
}
int mid=l+r>>1;
if(ul<=mid)modify(l,mid,ul,ur,id,ls(p));
if(ur>mid)modify(mid+1,r,ul,ur,id,rs(p));
}
inline bool fix(int p,int id){
int res=0;
while(!stac(p).empty()){
if(del[stac(p).back()])stac(p).pop_back();
else if(check(stac(p).back(),id)){
int u=stac(p).back();
del[u]=1;
merge(id,u);
res=1;
stac(p).pop_back();
}
else break;
}
return res;
}
inline bool update(int l,int r,int ul,int ur,int id,int p){
bool f=0;
while(!sav(p).empty()){
if(del[sav(p).back()])sav(p).pop_back();
else if(check(sav(p).back(),id)){
int u=sav(p).back();
del[u]=1;
merge(id,u);
f=1;
sav(p).pop_back();
}
else break;
}
if(f)return 1;
if(l>=ul&&r<=ur)return fix(p,id);
int mid=l+r>>1;
int res=0;
if(ul<=mid)res|=update(l,mid,ul,ur,id,ls(p));
if(ur>mid)res|=update(mid+1,r,ul,ur,id,rs(p));
return res;
}
}ST;
signed main(){
// freopen("T1i.in","r",stdin);
//freopen("T1o.out","w",stdout);
n=read();
for(int i=1;i<=n;i++){
sq[i].x1=read();
sq[i].x2=read();
sq[i].y1=read();
sq[i].y2=read();
hp[++tot]=sq[i].x1,hp[++tot]=sq[i].x2;
}
sort(hp+1,hp+1+tot);
for(int i=1;i<=tot;i++){
if(!refl[hp[i]])refl[hp[i]]=++cnt,lfer[cnt]=hp[i];
}
sort(sq+1,sq+1+n,cmp);
for(int i=1;i<=n;i++){
sq[i].x1=refl[sq[i].x1];
sq[i].x2=refl[sq[i].x2];
while(ST.update(1,cnt,sq[i].x1,sq[i].x2,i,1));
ST.modify(1,cnt,sq[i].x1,sq[i].x2,i,1);
}
for(int i=1;i<=n;i++){
if(del[i])continue;
ans[++top]=sq[i];
ans[top].x1=lfer[ans[top].x1];
ans[top].x2=lfer[ans[top].x2];
}
sort(ans+1,ans+1+top);
writeln(top);
for(int i=1;i<=top;i++)writek(ans[i].x1),writek(ans[i].x2),writek(ans[i].y1),writeln(ans[i].y2);
return 0;
}
/*
5
7 8 1 4
1 5 2 3
4 5 2 7
2 3 5 9
4 6 8 9
*/
B.雪雀
就是问你一个
考虑分治解决这个问题,每次从
后面忘了,嘻嘻。
C.燕鸥
这个是打表找规律题。
假设不进行钦定发现序列会以一个
然后就是到
省选模拟7
100+50+0,致敬传奇模拟赛之排行榜分数极差15pts。
A.电车
改一个非质数的话质数那块也是要改的,所以直接考虑质数的互换,两个质数能交换当且仅当在
B.波长
大便。
设
第二个就是可以选择对一段连跳若干个
进而答案的取值变为若干个一次函数形成一个下凸包
综上需要:一个线凸包的维护和一个支持查最大子段和和区间反转的数据结构,考虑用线段树维护区间最大子段和和区间最小子段和答案所在区间下标和反转tag,std给了一种非常简洁的实现。
#include<bits/stdc++.h>//34390
#define int long long
#define MAXN 100005
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define db long double
using namespace std;
int n,a[MAXN];
const int mod=998244353;
int K;
struct Segment_Tree{
struct data{
int v,l,r;
data(){}
inline data(int _v,int _l,int _r){v=_v,l=_l,r=_r;}
inline bool operator<(const data &X)const{return v<X.v;}
inline data operator+(const data &X)const{return data(v+X.v,l,X.r);}
};
struct seg{
data sum,lans,rans,xv;
seg(){}
inline seg(data _s,data _l,data _r,data _x){sum=_s,lans=_l,rans=_r,xv=_x;}
inline seg operator+(const seg &X)const{
return seg(sum+X.sum,max(lans,sum+X.lans),max(X.rans,rans+X.sum),max({xv,X.xv,rans+X.lans}));
}
};
struct Node{
Node *ls,*rs;
bool tag;seg X,N;
inline void push_up(){assert(ls&&rs);X=ls->X+rs->X,N=ls->N+rs->N;}
inline void down(){swap(X,N),tag^=1;}
inline void spread(){if(!tag)return ;ls->down(),rs->down(),tag=0;}
};
Node *rt;
vector<Node>tree;
inline void build(int l,int r,Node *&p){
tree.emplace_back();
p=&tree.back();
if(l==r){
p->X.sum=p->X.lans=p->X.rans=p->X.xv=data(a[l],l,l);
p->N.sum=p->N.lans=p->N.rans=p->N.xv=data(-a[l],l,l);
return ;
}
int mid=l+r>>1;
build(l,mid,p->ls);
build(mid+1,r,p->rs);
p->push_up();
}
inline void modify(int l,int r,int ul,int ur,Node *p){
if(l>=ul&&r<=ur){p->down();return ;}
p->spread();
int mid=l+r>>1;
if(ul<=mid)modify(l,mid,ul,ur,p->ls);
if(ur>mid)modify(mid+1,r,ul,ur,p->rs);
p->push_up();
}
}ST;
vector<pii>sav;
int ans;
inline void insert(int k,int b){//convexhull maintain
while(sav.size()>1){
pii A=sav.back(),B=sav[sav.size()-2];
db V=(db)(A.se-B.se)/(A.fi-B.fi);
if(V*k-b<=V*A.fi-A.se)sav.pop_back();
else break;
}
sav.emplace_back(mp(k,b));
}
signed main(){
// freopen("hacho14.in","r",stdin);
scanf("%lld%lld",&n,&K);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
ST.tree.reserve(2*n+5);
ST.tree.clear();
ST.build(1,n,ST.rt);
// printf("ced\n");
sav.emplace_back(mp(0,0));
int w=0;
for(int i=1;i<=n;i++){
if(ST.rt->X.xv.v<=0)break;
w+=ST.rt->X.xv.v;
insert(i,w);
ST.modify(1,n,ST.rt->X.xv.l,ST.rt->X.xv.r,ST.rt);
}
sort(a,a+1+n);
reverse(a,a+1+n);
for(int i=sav.back().fi+1;i<=n;i++)
if(a[i]<=0){
w+=a[i],insert(i,w);
}
// for(int i=0;i<sav.size();i++)if(sav[i].fi==34390){
// printf("What the fuck\n");
// return 0;
// }
int L=ceil((db)(sav.back().se-K)/sav.back().fi),R=0;
bool flag=0;
while(sav.size()>1){
pii A=sav.back();
sav.pop_back();
pii B=sav.back();
db V=(db)(A.se-B.se)/(A.fi-B.fi);
if (V<L){
L=ceil((db)(B.se-K)/B.fi);
continue;
}
R=ceil(V);
if(L<R){
if(!flag)ans=(K+(A.fi*L-A.se))%mod*L%mod,flag=1;
ans=(ans+(__int128)(L+R+1)*(R-L)/2%mod*A.fi-R+L)%mod;
}
L=R;
}
printf("%lld\n",(ans%mod+mod)%mod);
return 0;
}
C.捕获
没改嘻嘻。
省选模拟8
30+20+0,网络流专题说是。
AGC031E
题面是二改的,令人忍俊不禁。
限制和数据范围是难以处理的,所以用网络流解决(?),比如现在考虑限制
话是这么说的但是网络流没法四个维度一起考虑。考虑枚举选了
所以你考虑两个维度进行坐标的选,大概是这么个意思。
CF1572D
显然匹配点形成二分图,按二进制1个数奇偶性分组之后左部点连一些右部点和汇点然后右部点连一个和超汇有K流量限制的假汇点就可以费用流。
但是边数严重超时,发现一个人匹配后会踢掉
CF708D
一直以为给的图是一个最大流图哈哈。现在看是简单题不知道为啥考场上没一点想法。
考虑如何将原图修改成可行的。对于
注意原图中的流量是必须保证的,建完图之后跑最小费用可行流。
省选模拟9
喜欢出原题。
100+70+100
A.染色
维护黑点到根的路径,节点答案可以用换根求出,换根式子内的系数可以用树剖维护,比原题简单。
B.寝室管理
对于树的情况:点分治后树状数组即可,也可以用多项式卷一下,没必要。
对于基环树的情况,拆一条环边然后维护每个树的答案,每个树之间按距离乘一下就完事了。
C.基因合成
原题。[CERC2014]Virus synthesis。
省选模拟10
40+100+20
A.矩形
你先敲个50pts,把维护子段和的线段树改成可持久化树,一行一个树然后set维护行最大连续区间*最大高度。挂分。
B.往事
喜欢出原题,记一下。
求Trie树上lcs+lca深度(lcp)和的最大值。给Trie建一个广义SAM,根据性质parent树上两点的lcs就是他们的lca长度,建完跑lca然后存一下可能作为答案的点跑set启发式合并,set按dfn排完序可以直接找前后继求答案。
C.时空穿梭
第一道场上出正解的数论,但是让卡常了,而且正解没时间打嘻嘻。
枚举左下右上端点
考虑二维的情况,答案应为
带入然后指标换一下
设
发现这个东西分为
后面一部分可以杨辉三角后调和级数预处理,答案扩展到
这样就可以
省选模拟11
0(100)+5+5
A.划分
卧槽比赛好难没分卧槽我他妈跟t1爆了卧槽我t1切了卧槽出分了卧槽ub是什么歌??卧槽rk1->rk倒1不嘻嘻。
这个看着就特别不能dp,还是二进制最优化题那就考虑贪心,经过构造发现,假设
B.树
不会。
C.划分树
省选模拟12
0+100+15
A.逆序对
白送56但是来不及写了嘻嘻。
就是你先写一个暴力状压,然后时空爆炸,然后考虑优化状态数,可以通过诡异的数学证明把状态数干成
B.网格图
这不是我们燕鸥吗怎么跑到这了。
n=3之前写了,n=2的话一样考虑分治,然后上面的较优当且仅当满足一个偏序上树状数组,n=2本来跑的就快拿最短路写得了就别分讨大dp了。
C.种苹果
诡异题目,这个叫做大容量小值域背包,因为物品大小只有12345然后直接对余数分类来dp,这个东西还满足决策单调性你写个单队就行,也可以整体二分,后者比较短。
#include<bits/stdc++.h>
#define MAXN 300005
#define int long long
using namespace std;
const int mod=998244353,bas=20201205;
int n,b[MAXN],V,ans;
int dp[6][5*MAXN];
vector<int>W[6],sum[6];
inline void dfs(int now,int lst,int l,int r,int L,int R){
if(L>R)return;
int mid=L+R>>1,k=-1;
for(int i=max(l,mid-(int)W[now].size()),xr=min(mid,r);i<=xr;i++)
if(k==-1||dp[now-1][i*now+lst]+sum[now][mid-i]>dp[now-1][k*now+lst]+sum[now][mid-k])k=i;
dp[now][mid*now+lst]=dp[now-1][k*now+lst]+sum[now][mid-k];
dfs(now,lst,l,k,L,mid-1);
dfs(now,lst,k,r,mid+1,R);
}
signed main(){
freopen("apple.in","r",stdin);
freopen("apple.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&b[i]),V+=b[i];
for(int i=1,c;i<=n;i++){
scanf("%lld",&c);
W[b[i]].emplace_back(c);
}
for(int i=1;i<=5;i++){
sort(W[i].begin(),W[i].end(),greater<>());
int s=0;
sum[i].emplace_back(0);
for(int x:W[i])s+=x,sum[i].emplace_back(s);
}
for(int i=1;i<=5;i++){
for (int r=0;r<i;r++)dfs(i,r,0,(V-r)/i,0,(V-r)/i);
}
int hp=1;
for (int i=0;i<=V;i++){
hp=hp*bas%mod;
ans=(ans+hp*(dp[5][i]%mod)%mod%mod+mod)%mod;
}
printf("%lld",ans%mod);
return 0;
}
省选模拟13
65+24+40
A.区间
有一个事情就是记区间最大值为
树上全点对路径问题考虑点分治怎么数组上子序列问题就忘了分治了,我是傻逼吗。对于很大的值的取等可以用hash处理,光速幂预处理幂次。然后就是在序列上分治,跑出来一个最大值然后考虑短的一侧对长侧的贡献。按上面的结论直接枚举可能取等的和然后hash判贡献。
B.圣诞树
考场上一秒想到2sat,然后我一想感觉方案是难以得到的然后弃了。哈哈。
考虑这样的建模:设
连边和逆反命题,对每个点从根往下一直找合法的儿子直到不能跳为止。
C.神奇国度
没看懂。
省选模拟14
70+10(60)+0
打的啥。
A.数
坠机题,开考之后急的要死两个小时没分,糊了个70走人了,不知道在急什么。
列个不等式然后两边式子一提,贡献可以枚举
B.树
一眼轮状病毒+polya。答案就是
需要一些手法,比如那个 不难发现
书
不难,但是没改。
省选模拟15
60+0(10)+0(20)
喜欢打完暴力不拍。
A.单峰序列
60pts可以贪心,考场上一直在写假,但是最后还是真了要不然保龄了。
从小到大填每次考虑向左向右较小的那个放。考虑动态加入的过程对左侧是没有影响的,右侧则递增,对于新加入的这个值影响的也是大于它的那一部分,状态的影响其实可以用一个线段树均摊实现。
B.划分线段
这个是树,但是合并就比较恶心了,因为贡献的合并或者最优化没法直接在当前节点处理,然后tj给出了一个比较牛逼的操作就是设
对于叶子节点的初始化有
dp[u][0][0][0]=sav[u].se-sav[u].fi;
dp[u][0][0][1]=-sav[u].fi;
dp[u][0][1][0]=sav[u].se;
dp[u][0][1][1]=0;
第二维为了省空间自减了一个
对于合并两个子树
另外转移到最后一个子树时要进行消预留段的操作就是
if(x)dp[u][i][0][y]=max(dp[u][i][0][y],g[typ][i][x][y]-sav[u].fi);
if(y)dp[u][i][x][0]=max(dp[u][i][x][0],g[typ][i][x][y]+sav[u].se);
还有一个比较手法的补足操作
if(x||y)dp[u][i][x][y]=max(dp[u][i][x][y],g[typ][i][x][y]);
这个的意思是,如果我的两侧有至少一个待补足的段,我可以让它们在我中间就被补足,从而留下来至少一个空缺的段,此时有 x||y
的前置条件。
C.红蓝树
想这样一个事情:每个右括号的颜色都会影响到它右侧的第一个左括号的颜色,显然这是一个多对一的关系,进而这样的影响关系当形成一棵树,如果
先预处理出这个树,并每次遇到一个新的左括号就新开一个节点同时也是这个节点的
现在不考虑反转颜色的操作,则栈信息可以用线段树来合并,具体地对于一个dfn 小的左子树和大的右子树(认为子树已经合并完成),考虑对每个节点维护区间内最小和最大的蓝色点dfn,即答案,则合并时相当于是额外加了左子树最大->右子树最小这一段的dfn的蓝色点贡献,这一部分可以直接倍增求出。
inline int getval(int l,int r){
int res=1;
if(!l)return 0;
for(int i=MAXK-1;i>=0;i--)if(father[i][l]<r)res+=(1<<i),l=father[i][l];
return res;
}
然后就可以合并了,查询的时候查一下两端最近的有效点的答案,最后把剩下来的一段
对于有修改的情况,你会发现这个线段树的维护性能有点过于牛逼了,直接同时维护红点和蓝点的信息,反转的时候打个rev标记就行,然后就做出来了。
关于一个corner case:当且仅当两节点都有蓝点的时候才进行合并贡献,否则只进行最大最小值的维护和答案简单相加,知道某一次出现两侧都有蓝点时才进行合并,如果自始至终只有左侧某一个蓝点则会在最后对
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律