成都讲题总结
Day1
Gym101365F
按照题意用平衡树维护即可,操作本质上是平衡树上二分和删除前 \(k\) 大。
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define int long long
using namespace std;
const int maxn = 1e5+114;
struct Node{
long long val,sz,ls,rs,cnt,t;
long long sum;//sum 维护价格
}treap[maxn];
int tot;
int rt;
int clone(int t,int cnt){
int New=++tot;
treap[New].val=rand();
treap[New].sz=cnt;
treap[New].ls=0;
treap[New].rs=0;
treap[New].cnt=cnt;
treap[New].t=t;
treap[New].sum=t*cnt;
return New;
}
inline void pushup(int cur){
treap[cur].sz=treap[treap[cur].ls].sz+treap[treap[cur].rs].sz+treap[cur].cnt;
treap[cur].sum=treap[treap[cur].ls].sum+treap[treap[cur].rs].sum+treap[cur].t*treap[cur].cnt;
}
inline int merge(int x,int y){
if (!x||!y) return x+y;
if (treap[x].val<treap[y].val)
{
treap[x].rs=merge(treap[x].rs,y);
pushup(x);
return x;
}
else
{
treap[y].ls=merge(x,treap[y].ls);
pushup(y);
return y;
}
}
inline void split(int cur,int x,int &l,int &r) {
if(cur==0){
l=r=0;
return ;
}
if(treap[cur].t>x){
r=cur;
split(treap[cur].ls,x,l,treap[cur].ls);
}
else{
l=cur;
split(treap[cur].rs,x,treap[cur].rs,r);
}
pushup(cur);
}
inline int find(int ft,int cur){
if(cur==0) return 0;//节点不存在
if(treap[cur].t==ft) return cur;
if(treap[cur].t<ft) return find(ft,treap[cur].rs);
else return find(ft,treap[cur].ls);
}
inline void add(int ft,int cur,int val){
if(cur==0) return ;
if(treap[cur].t==ft){
treap[cur].cnt+=val;
pushup(cur);
return ;
}
if(treap[cur].t<ft) add(ft,treap[cur].rs,val);
else add(ft,treap[cur].ls,val);
pushup(cur);
}
inline void insert(int t,int cnt){
if(find(t,rt)!=0){
add(t,rt,cnt);
}
else{
int x=clone(t,cnt);
int a=0,b=0;
split(rt,t,a,b);
rt=merge(merge(a,x),b);
}
}
inline int kth(int cur,int k){
if(k==0||cur==0){
return 0;
}
if(k<=treap[treap[cur].ls].sz){
return kth(treap[cur].ls,k);
}
else{
return treap[cur].t*(min(treap[cur].cnt,k-treap[treap[cur].ls].sz))+treap[treap[cur].ls].sum+kth(treap[cur].rs,max(0*1ll,k-treap[treap[cur].ls].sz-treap[cur].cnt));
}
}
inline void clear(int cur,int k){//卖掉前 k 个雪糕
if(k==0||cur==0){
return ;
}
if(k<=treap[treap[cur].ls].sz){
clear(treap[cur].ls,k);
}
else{
clear(treap[cur].rs,max(0*1ll,k-treap[treap[cur].ls].sz-treap[cur].cnt));
treap[cur].cnt-=min(treap[cur].cnt,k-treap[treap[cur].ls].sz);
treap[cur].ls=0;//把右子树全部丢掉
}
pushup(cur);
}
inline void dfs(int cur){
if(cur==0) return ;
dfs(treap[cur].ls);
cout<<treap[cur].t<<' '<<treap[cur].sz<<'\n';
dfs(treap[cur].rs);
}
//查错
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
srand(time(0));
string opt;
int n,c;
while(cin>>opt>>n>>c){
if(opt=="ARRIVE"){
insert(c,n);
}
else{
if(n>treap[rt].sz){
cout<<"UNHAPPY\n";
continue;
}
int val=kth(rt,n);
if(val<=c){
cout<<"HAPPY\n";
clear(rt,n);
}
else{
cout<<"UNHAPPY\n";
}
}
}
return 0;
}
CodeForces620E
因为颜色只有又或者没有两种状态,所以考虑状压下来,然后用线段树维护即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+114;
int n,q;
struct Node{
bitset<65> col;
int tag;
}tree[maxn<<2];
void pushup(int cur){
tree[cur].col=(tree[cur<<1].col|tree[cur<<1|1].col);
}
void pushdown(int cur){
if(tree[cur].tag!=0){
tree[cur<<1].col.reset();
tree[cur<<1].col[tree[cur].tag]=1;
tree[cur<<1|1].col.reset();
tree[cur<<1|1].col[tree[cur].tag]=1;
tree[cur<<1].tag=tree[cur<<1|1].tag=tree[cur].tag;
tree[cur].tag=0;
}
}
void cover(int cur,int lt,int rt,int l,int r,int c){
if(l<=lt&&rt<=r){
tree[cur].col.reset();
tree[cur].col[c]=1;
tree[cur].tag=c;
return ;
}
if(l>rt||r<lt) return ;
int mid=(lt+rt)>>1;
pushdown(cur);
cover(cur<<1,lt,mid,l,r,c);
cover(cur<<1|1,mid+1,rt,l,r,c);
pushup(cur);
}
bitset<65> answer;
void query(int cur,int lt,int rt,int l,int r){
if(l<=lt&&rt<=r){
answer|=tree[cur].col;
return ;
}
if(l>rt||r<lt) return ;
if(l>rt||r<lt) return ;
int mid=(lt+rt)>>1;
pushdown(cur);
query(cur<<1,lt,mid,l,r);
query(cur<<1|1,mid+1,rt,l,r);
}
int dfn[maxn];
int mx[maxn];
int col[maxn];
vector<int> edge[maxn];
inline void add(int u,int v){
edge[u].push_back(v);
edge[v].push_back(u);
}
int tot;
void dfs(int u,int fa){
mx[u]=dfn[u]=++tot;
cover(1,1,n,dfn[u],dfn[u],col[u]);
for(int v:edge[u]){
if(v==fa) continue;
dfs(v,u);
mx[u]=max(mx[u],mx[v]);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>col[i];
}
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
add(u,v);
}
dfs(1,0);
while(q--){
int opt;
cin>>opt;
if(opt==1){
int x,c;
cin>>x>>c;
cover(1,1,n,dfn[x],mx[x],c);
}
else{
int x;
cin>>x;
answer.reset();
query(1,1,n,dfn[x],mx[x]);
cout<<answer.count()<<'\n';
}
}
}
CF487E
建出圆方树,令方点权值为其子节点权值最小值,问题转化为点修链最小值。
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
const int maxn = 1e5+114;
int tree[maxn<<4];
inline void pushup(int cur){
tree[cur]=min(tree[cur<<1],tree[cur<<1|1]);
}
inline void change(int cur,int lt,int rt,int pos,int c){
if(lt<=pos&&pos<=rt&<==rt){
tree[cur]=c;
return ;
}
if(pos>rt||pos<lt) return ;
int mid=(lt+rt)>>1;
change(cur<<1,lt,mid,pos,c);
change(cur<<1|1,mid+1,rt,pos,c);
pushup(cur);
}
inline int query(int cur,int lt,int rt,int l,int r){
if(l<=lt&&rt<=r){
return tree[cur];
}
if(r<lt||l>rt) return INT_MAX;
int mid=(lt+rt)>>1;
int answer=INT_MAX;
answer=min(answer,query(cur<<1,lt,mid,l,r));
answer=min(answer,query(cur<<1|1,mid+1,rt,l,r));
return answer;
}
int n,m,q,cnt;
std::vector<int> G[maxn],T[maxn<<1];
int dfn[maxn], low[maxn], dfc;
int stk[maxn],tp;
multiset<int> f[maxn<<1];
int value[maxn<<1];
void Tarjan(int u) {
low[u] = dfn[u] = ++dfc;
stk[++tp] = u;
for (int v : G[u]){
if (!dfn[v]) {
Tarjan(v);
low[u] = std::min(low[u], low[v]);
if (low[v] == dfn[u]) {
++cnt;
for (int x = 0; x != v; --tp) {
x = stk[tp];
T[cnt].push_back(x);
T[x].push_back(cnt);
}
T[cnt].push_back(u);
T[u].push_back(cnt);
}
}else
low[u] = std::min(low[u], dfn[v]);
}
}
void build(){
cnt=n;
for(int u=1;u<=n;u++){
if(!dfn[u]) Tarjan(u), --tp;
}
}
int sz[maxn<<1],son[maxn<<1],depth[maxn<<1],DFN[maxn<<1],tot,top[maxn<<1],father[maxn<<1];
void dfs1(int u,int fa){
father[u]=fa;
sz[u]=1;
depth[u]=depth[fa]+1;
for(int v:T[u]){
if(v==fa) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]]){
son[u]=v;
}
}
}
void dfs2(int u,int fa,int t){
DFN[u]=++tot;
top[u]=t;
if(son[u]!=0){
dfs2(son[u],u,t);
if(u>n) f[u].insert(value[son[u]]);
}
for(int v:T[u]){
if(v==fa||v==son[u]) continue;
dfs2(v,u,v);
if(u>n){
f[u].insert(value[v]);
}
}
if(u<=n){
change(1,1,cnt,DFN[u],value[u]);
}
else{
change(1,1,cnt,DFN[u],(*f[u].begin()));
}
}
void chifan(int u,int v){
if(father[u]!=0)
f[father[u]].erase(f[father[u]].find(value[u]));
value[u]=v;
if(father[u]!=0)
f[father[u]].insert(value[u]);
change(1,1,cnt,DFN[u],value[u]);
if(father[u]!=0)
change(1,1,cnt,DFN[father[u]],(*f[father[u]].begin()));
}
int ask(int u,int v){
int res=INT_MAX;
while(top[u]!=top[v]){
if(depth[top[u]]<depth[top[v]]) swap(u,v);
res=min(res,query(1,1,cnt,DFN[top[u]],DFN[u]));
u=father[top[u]];
}
if(depth[u]<depth[v]) swap(u,v);
res=min(res,query(1,1,cnt,DFN[v],DFN[u]));
if(v>n){
res=min(res,value[father[v]]);
}
return res;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>q;
for(int i=1;i<=n;i++) cin>>value[i];
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
build();
for(int i=0;i<maxn*8;i++) tree[i]=INT_MAX;
dfs1(1,0);
dfs2(1,0,1);
while(q--){
char opt;
cin>>opt;
if(opt=='C'){
int a,w;
cin>>a>>w;
chifan(a,w);
}
else{
int u,v;
cin>>u>>v;
cout<<ask(u,v)<<'\n';
}
}
}
/*
6 7 5
4
2
1
9
7
6
2 1
1 3
2 3
1 4
5 1
4 5
4 6
C 6 2
A 5 4
A 5 6
A 4 1
C 1 5
*/
CF319E
考虑把相交的区间连边,然后并查集判断,然后考虑线段树优化建图即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+114;
int L[maxn],R[maxn];
int root;
int fa[maxn];
int found(int u){
if(fa[u]==u) return u;
else return (fa[u]=found(fa[u]));
}
struct Node{
int rt;
int ls,rs;
vector<int> s;
}tree[maxn*30];
int tot;
void pushdown(int &cur,int lt,int rt,int pos,int c){
if(cur==0) cur=++tot;
for(int it:tree[cur].s){
L[c]=min(L[c],L[found(it)]);
R[c]=max(R[c],R[found(it)]);
fa[found(it)]=c;
}
if(tree[cur].s.size()){
tree[cur].s.clear();
tree[cur].s.push_back(c);
}
if(lt==rt) return ;
int mid=(lt+rt-1)>>1;
if(mid>=pos) pushdown(tree[cur].ls,lt,mid,pos,c);
else pushdown(tree[cur].rs,mid+1,rt,pos,c);
}
void add(int &cur,int lt,int rt,int l,int r,int c){
if(l>rt||r<lt) return ;
if(cur==0) cur=++tot;
if(l<=lt&&rt<=r){
tree[cur].s.push_back(c);
return ;
}
int mid=(lt+rt-1)>>1;
add(tree[cur].ls,lt,mid,l,r,c);
add(tree[cur].rs,mid+1,rt,l,r,c);
}
int q,cnt;
int main(){
cin>>q;
for(int i=0;i<=q;i++) fa[i]=i;
while(q--){
int opt,l,r;
cin>>opt>>l>>r;
if(opt==1){
cnt++;
L[cnt]=l,R[cnt]=r;
pushdown(root,-1000000000,1000000000,l,cnt);
pushdown(root,-1000000000,1000000000,r,cnt);
if(r==l+1){
continue;
}
add(root,-1000000000,1000000000,l+1,r-1,cnt);
}
else{
l=found(l),r=found(r);
if(l==r) cout<<"YES\n";
else if((L[r]<L[l]&&L[l]<R[r])||(L[r]<R[l]&&R[l]<R[r])) cout<<"YES\n";
else cout<<"NO\n";
}
}
}
Gym100739E
先把切比雪夫距离转化成曼哈顿距离,然后拆绝对值式子用平衡树维护即可。
#include<bits/stdc++.h>
#define int __int128
namespace IO{
const int SIZE=1<<21;
static char ibuf[SIZE],obuf[SIZE],*iS,*iT,*oS=obuf,*oT=oS+SIZE-1;
int qr;
char qu[55],c;
bool f;
#define getchar() (IO::iS==IO::iT?(IO::iT=(IO::iS=IO::ibuf)+fread(IO::ibuf,1,IO::SIZE,stdin),(IO::iS==IO::iT?EOF:*IO::iS++)):*IO::iS++)
#define putchar(x) *IO::oS++=x,IO::oS==IO::oT?flush():0
#define flush() fwrite(IO::obuf,1,IO::oS-IO::obuf,stdout),IO::oS=IO::obuf
#define puts(x) IO::Puts(x)
template<typename T>
inline void read(T&x){
for(f=1,c=getchar();c<48||c>57;c=getchar())f^=c=='-';
for(x=0;c<=57&&c>=48;c=getchar()) x=(x<<1)+(x<<3)+(c&15);
x=f?x:-x;
}
template<typename T>
inline void write(T x){
if(!x) putchar(48); if(x<0) putchar('-'),x=-x;
while(x) qu[++qr]=x%10^48,x/=10;
while(qr) putchar(qu[qr--]);
}
inline void Puts(const char*s){
for(int i=0;s[i];i++)
putchar(s[i]);
putchar('\n');
}
struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}
using IO::read;
using IO::write;
using namespace std;
const int maxn = 2e5+114;
stack<int> s[2];
struct Node{
int val,sz,ls,rs,t,sum;
}treap[maxn][2];
int tot[2];
int rt[2];
int clone(int t,int type){
int New;
if(s[type].size()!=0){
New=s[type].top();
s[type].pop();
}
else
New=++tot[type];
treap[New][type].val=rand();
treap[New][type].sz=1;
treap[New][type].ls=0;
treap[New][type].rs=0;
treap[New][type].t=t;
treap[New][type].sum=t;
return New;
}
inline void pushup(int cur,int type){
treap[cur][type].sz=treap[treap[cur][type].ls][type].sz+treap[treap[cur][type].rs][type].sz+1;
treap[cur][type].sum=treap[treap[cur][type].ls][type].sum+treap[treap[cur][type].rs][type].sum+treap[cur][type].t;
}
inline int merge(int x,int y,int type){
if (!x||!y) return x+y;
if (treap[x][type].val<treap[y][type].val)
{
treap[x][type].rs=merge(treap[x][type].rs,y,type);
pushup(x,type);
return x;
}
else
{
treap[y][type].ls=merge(x,treap[y][type].ls,type);
pushup(y,type);
return y;
}
}
inline void split(int cur,int x,int &l,int &r,int type) {
if(cur==0){
l=r=0;
return ;
}
if(treap[cur][type].t>x){
r=cur;
split(treap[cur][type].ls,x,l,treap[cur][type].ls,type);
}
else{
l=cur;
split(treap[cur][type].rs,x,treap[cur][type].rs,r,type);
}
pushup(cur,type);
}
inline void insert(int t,int type){
int a=0,b=0,c=0;
split(rt[type],t,a,c,type);
b=clone(t,type);
rt[type]=merge(merge(a,b,type),c,type);
}
inline void erase(int t,int type){
int a=0,b=0,c=0;
split(rt[type],t,a,c,type);
split(a,t-1,a,b,type);
s[type].push(b);
b=merge(treap[b][type].ls,treap[b][type].rs,type);
rt[type]=merge(merge(a,b,type),c,type);
}
inline int query(int x,int type){
int a=0,b=0;
split(rt[type],x,a,b,type);
int res=treap[a][type].sz*x-treap[a][type].sum+treap[b][type].sum-treap[b][type].sz*x;
rt[type]=merge(a,b,type);
return res;
}
int n,q,base,last;
int X[maxn],Y[maxn];
signed main(){
srand(time(0));
read(n);
read(q);
read(base);
for(int i=1;i<=n;i++){
int x,y;
read(x);
read(y);
X[i]=x;
Y[i]=y;
insert(x+y,0);
insert(x-y,1);
}
while(q--){
int opt;
read(opt);
if(opt==0){
int pos;
read(pos);
erase(X[pos]+Y[pos],0);
erase(X[pos]-Y[pos],1);
int a1,b1,a2,b2;
read(a1);
read(b1);
read(a2);
read(b2);
int x=(a1*last+b1)%base;
int y=(a2*last+b2)%base;
X[pos]=x;
Y[pos]=y;
insert(X[pos]+Y[pos],0);
insert(X[pos]-Y[pos],1);
}
else{
int a1,b1,a2,b2;
read(a1);
read(b1);
read(a2);
read(b2);
int x=(a1*last+b1)%base;
int y=(a2*last+b2)%base;
write(last=(query(x+y,0)+query(x-y,1)+(n-1)*2));
putchar('\n');
}
}
}
Day2
CF1093E
考虑转换成一个二维数点,然后发现这题我之前出过(?
#include<bits/stdc++.h>
using namespace std;
const int maxn = 6e5+114;
const int maxsq = 500;
const int warma = 447;
int n,cnt;
struct Node{
int opt,x,l,r,L,R;
}Q[maxn];
int anser[maxn];
pair<int,int> wyb[maxn],zbz[maxn];//处理时的数组 & 原本的数组
int val(int l1,int r1,int l2,int r2){//计算两个区间的交集大小
int mxl=max(l1,l2),mir=min(r1,r2);
if(mxl>mir) return 0;
else return (mir-mxl+1);
}
class block{//O(\sqrt n) 区间修改 O(1) 区间查询
private:
int bl_pre[maxsq][maxsq];//块内前缀和
int tag[maxsq];//块内标记
int pre[maxsq];//块间前缀和
public:
void add(int l,int r,int v);
int query(int l,int r);
void init();
}chifan;
void block::init(){
for(int i=0;i<maxsq;i++)
for(int j=0;j<maxsq;j++)
bl_pre[i][j]=tag[i]=pre[i]=0;
}
void block::add(int l,int r,int v){
int bl=l/warma;
int lpos=l%warma;
if(lpos==0){
lpos=warma;
bl--;
}
int br=r/warma;
int rpos=r%warma;
if(rpos==0){
rpos=warma;
br--;
}
//整块处理
if(bl==br){
for(int i=lpos;i<=rpos;i++){
bl_pre[bl][i]+=v*(i-lpos+1);
}
for(int i=rpos+1;i<=warma;i++){
bl_pre[bl][i]+=v*(rpos-lpos+1);
}
pre[0]=bl_pre[0][warma];
for(int i=1;i<=warma;i++){
pre[i]=pre[i-1]+bl_pre[i][warma]+tag[i]*warma;
}
return ;
}
for(int i=bl+1;i<=br-1;i++){
tag[i]+=v;
}
//头部残块处理
for(int i=lpos;i<=warma;i++){
bl_pre[bl][i]+=v*(i-lpos+1);
}
//尾部残块处理
for(int i=1;i<=rpos;i++){
bl_pre[br][i]+=v*i;
}
for(int i=rpos+1;i<=warma;i++){
bl_pre[br][i]+=v*rpos;
}
//块间处理
pre[0]=bl_pre[0][warma];
for(int i=1;i<=warma;i++){
pre[i]=pre[i-1]+bl_pre[i][warma]+tag[i]*warma;
}
return ;
}
int block::query(int l,int r){
int bl=l/warma;
int lpos=l%warma;
if(lpos==0){
lpos=warma;
bl--;
}
int br=r/warma;
int rpos=r%warma;
if(rpos==0){
rpos=warma;
br--;
}
if(bl==br){
return bl_pre[bl][rpos]-bl_pre[bl][lpos-1]+tag[bl]*(rpos-lpos+1);
}
int res=0;
res+=pre[br-1]-pre[bl];
res+=bl_pre[bl][warma]-bl_pre[bl][lpos-1]+(warma-lpos+1)*tag[bl];
res+=bl_pre[br][rpos]+tag[br]*rpos;
return res;
}
void work(int lt,int rt){//逐块处理
rt=min(rt,n);
chifan.init();
for(int i=1;i<=n;i++) wyb[i]=zbz[i];
for(int i=lt;i<=rt;i++){
chifan.add(wyb[i].first,wyb[i].second,1);
}
for(int i=1;i<=cnt;i++){
if(Q[i].opt==1){
int l=Q[i].l,r=Q[i].r,L=Q[i].L,R=Q[i].R;
if(l<=lt&&rt<=r){//被包含
anser[i]+=chifan.query(L,R);
}
else if(lt<=l&&r<=rt){//另外一种被包含
int res=0;
for(int i=l;i<=r;i++){
res+=val(wyb[i].first,wyb[i].second,L,R);
}
anser[i]+=res;
}
else if(rt>=r&&r>=lt){
int res=0;
for(int i=lt;i<=r;i++){
res+=val(wyb[i].first,wyb[i].second,L,R);
}
anser[i]+=res;
}
else if(lt<=l&&l<=rt){
int res=0;
for(int i=l;i<=rt;i++){
res+=val(wyb[i].first,wyb[i].second,L,R);
}
anser[i]+=res;
}
}
else{
int x=Q[i].x,l=Q[i].l,r=Q[i].r;
if(lt<=x&&x<=rt){
chifan.add(wyb[x].first,wyb[x].second,-1);
wyb[x].first=l,wyb[x].second=r;
chifan.add(wyb[x].first,wyb[x].second,1);
}
}
}
}
int sq;
int q;
int a[maxn],b[maxn];
int A[maxn],B[maxn];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>q;
sq=sqrt(n);
for(int i=1;i<=n;i++){
cin>>a[i];
A[a[i]]=i;
}
for(int i=1;i<=n;i++){
cin>>b[i];
B[b[i]]=i;
}
for(int i=1;i<=n;i++){
zbz[A[i]].first=B[i];
zbz[A[i]].second=B[i];
}
for(int i=1;i<=q;i++){
int OPT;
cin>>OPT;
if(OPT==1){
int la,ra,lb,rb;
cin>>la>>ra>>lb>>rb;
cnt++;
Q[cnt].opt=1;
Q[cnt].l=la;
Q[cnt].r=ra;
Q[cnt].L=lb;
Q[cnt].R=rb;
}
else{
int x,y;
cin>>x>>y;
//b[x] - A[b[x]] x -> A[b[x]] y
//b[y] - A[b[y]] y -> A[b[y]] x
//b[x] - b[y]
//B[b[x]]
cnt++;
Q[cnt].opt=2;
Q[cnt].x=A[b[x]];
Q[cnt].l=y;
Q[cnt].r=y;
cnt++;
Q[cnt].opt=2;
Q[cnt].x=A[b[y]];
Q[cnt].l=x;
Q[cnt].r=x;
swap(b[x],b[y]);
swap(B[b[x]],B[b[y]]);
}
}
int L=1,R=sq;
while(L<=n){
work(L,R);
L+=sq,R+=sq;
}
for(int i=1;i<=cnt;i++){
if(Q[i].opt==1) cout<<anser[i]<<'\n';
}
}
/*
6 2
5 1 4 2 3 6
2 5 3 1 4 6
2 2 4
1 2 3 3 5
*/
Strange Limit
暴力模拟加欧拉函数与循环节加速即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int fac[13];
int euler_phi(int n) {
int ans = n;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}
int _pow(int a,int b,int mod){
if(b==0) return 1;
if(b==1) return a;
int res=_pow(a,b/2,mod);
res=res*res%mod;
if(b%2==1) res=res*a%mod;
return res;
}
int p,m;
int solve(int x){
if(p%x==0) return _pow(p,p,x);
return _pow(p,solve(euler_phi(x)) + euler_phi(x),x);
}
signed main(){
freopen("limit.in","r",stdin);
freopen("limit.out","w",stdout);
fac[0]=1;
for(int i=1;i<=12;i++) fac[i]=fac[i-1]*i;
cin>>p>>m;
cout<<solve(fac[m])%fac[m]<<'\n';
}
GCD Determinant
考虑构造两个辅助矩阵外加莫比乌斯反演推导出答案就是所有数的欧拉函数之积。
#include <iostream>
#define int long long
#define mod 1000000007
using namespace std;
int euler_phi(int n) {
int ans = n;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}
signed main(){
int n;
while(cin>>n){
int res=1;
for(int i=1;i<=n;i++){
int x;
cin>>x;
res=(res*euler_phi(x))%mod;
}
cout<<res<<'\n';
}
}
Remainders Game
不难证明成立的充分必要条件为 \(k | lcm(c_i)\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e6+114;
int a[maxn];
int f=1;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
f=(f*a[i])/__gcd(f,a[i]);
f%=k;
}
cout<<(f==0?"Yes":"No");
}
Master of Phi
考虑莫比乌斯反演完之后推一个等比数列和。
#include<iostream>
#define int long long
#define mod 998244353
using namespace std;
int _pow(int a,int b){
if(b==0) return 1;
if(b==1) return a;
int res=_pow(a,b/2);
res=res*res%mod;
if(b%2==1) res=res*a%mod;
return res;
}
signed main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
int res=1;
for(int i=1;i<=n;i++){
int p,q;
cin>>p>>q;
res=(res*_pow(p,q-1)%mod)*(p+q*(p-1)%mod)%mod;
}
cout<<res<<'\n';
}
}
Day3
A
先看一眼题意
然后发现所谓的 \(T\) 就是两个 \(S\) 拼接起来
又因为在 \(U\) 中只插入一个字符,所以 \(S\) 一定是 \(U\) 的一个前缀或者后缀
又因为 \(S\) 的长度一定是 \(U\) 的长度减 \(1\) 除以 \(2\) 所以我们可以得到两个可能的 \(S\)
然后暴力判断即可
复杂度 \(O(len)\)
B
考虑就关注关系建边
考虑把类似于 \(B -> C 且 C-> B\) 的点缩成一个分量
那么假若一个点与分量中某一个点有 连过去的 边,必定可以在活动开始时与分量内所有点连边
我们把点集之间两两有边的点称为一个 团
那么只要我们动态维护团便可以统计答案
为了方便缩点,我们采用 set
维护边即可
注意这里要启发式合并团
总复杂度 \(O(n \log^2 n)\)
C
鉴定完毕,一眼 dp
先考虑设计状态 \(f[i][j]\) 表示烹饪盖饭前 \(i\) 步与烹饪咖喱前 \(j\) 步所能获得的最大得分
那么有一个谁都会的 \(n^2\) 转移
不过为了优化,还是把他写出来
令 \(pre[i][0/1]\) 表示烹饪盖饭/咖喱前 \(i\) 步总共需要多久
\(f[i][j]=\max(f[i-1][j]+[pre[i][0]+pre[j][1]<=S[i]] \times P[i],f[i][j-1]+[pre[i][0]+pre[j][1]<=T[j]] \times Q[j])\)
由于我很弱,所以这边考虑做一手 \(P[i] = Q[j] = 1\) 的数据
这个时候式子化简为:
\(f[i][j]=\max(f[i-1][j]+[pre[i][0]+pre[j][1]<=S[i]],f[i][j-1]+[pre[i][0]+pre[j][1]<=T[j]])\)
这个时候我们可以最直接向 \(S[i],T[j]\) 中更小的一项转移
复杂度 \(O(n + m)\)
可以骗到大约 \(49\) 分的好分数
D
我有一个很鬼畜的想法
我们计算骑士没走一步 \(x,y\) 期望增加多少,然后依次计算走出棋盘的期望步数(?
但是第一个样例就被 HACK 了
考后
双双挂到零分
反思
T1 没有对拍挂了一个特判, T2 挂了一个并查集
下次再不写对拍我请全场喝奶茶
Day4,5
T1
是一个很神奇的决策单调性优化 dp
,改完之后学会了一个分治模板。
void work(int l,int r,int L,int R){
if(l>r) return ;
int mid=(l+r)>>1,p=0;
int mx=0;
for(int i=L;i<=min(mid,R);i++) {
if(w(i,mid)>mx) mx=w(i,mid),p=i;
}
dp[mid]=max(dp[mid],mx);
work(l,mid-1,L,p);
work(mid+1,r,p,R);
}
T2
是一个离线猫树可以解决的问题,考场上没想出来说明我智力很低,下次再犯我就自罚 \(5\) 道同等类型的题目。
T3
居然比 T2 还简单,口胡一个结论建图跑最短路就可以了,考场上真没想到。
T4
不会。
Day114
忘记是第几天了QAQ
动物园
预处理前缀然后倍增。
字符串的匹配
设计哈希函数树状数组维护。
Sza-Template
处理出 border 然后 \(O(n \log n)\) 枚举。
阿狸的打字机
建出 AC 在自动机转化成树上问题树状数组解决。
Substrings in a String
预处理 bitset 压位暴力匹配。