来自aakennes的新年祝福
来自aakennes的新年祝福
组题人: @aakennes
\(A\) P888. 字符串会上树 \(AC\)
-
基础字符串。
点击查看代码
string s,t="",w=""; map<string,string>f; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,q,i; cin>>n>>q>>s; for(i=0;i<=n-1;i++) { if(s[i]=='&') { f[t]=w; t=""; } else if(s[i]=='=') w=""; else if('0'<=s[i]&&s[i]<='9') w+=s[i]; else t+=s[i]; } if(t!="") f[t]=w; for(i=1;i<=q;i++) { cin>>s; if(f.find(s)==f.end()) cout<<"Does not exist."<<endl; else cout<<f[s]<<endl; } return 0; }
\(B\) P889. 挖宝石 \(AC\)
-
背包循环移位转移。
点击查看代码
bool f[2][3010]; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,k,x,i,j; cin>>n>>k; for(i=1;i<=n;i++) { cin>>x; x%=k; for(j=0;j<=x-1;j++) f[i&1][j]=f[(i-1)&1][j]|f[(i-1)&1][(j-x+k)%k]; for(j=x;j<=k-1;j++) f[i&1][j]=f[(i-1)&1][j]|f[(i-1)&1][j-x]; f[i&1][x]=1; if(f[i&1][0]==1) { cout<<"Yes"<<endl; return 0; } } cout<<"No"<<endl; return 0; }
\(C\) P890. 电梯系统 \(AC\)
-
手摸样例,得到一种构造方式形如
1->2->3->4->5->6->7 1---->3---->5---->7 1------->4---->6 1---------->5 1------------->6 1---------------->7 2---->4------->7 2------->5 2---------->6 2------------->7 3------>6 3------>7
-
可知 \(\sum\limits_{i=1}^{n}\max(0,n-(2i-1))\) 即为所求。
点击查看代码
int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,ans=0,i; cin>>n; for(i=1;i<=n;i++) { if(2*i-1<=n) ans+=n-(2*i-1); } cout<<ans<<endl; return 0; }
\(D\) P891. 打游戏 \(AC\)
-
和求树的直径一样做即可。
点击查看代码
struct node { ll nxt,to,w; }e[200010]; ll head[100010],a[100010],f[100010],g[100010],len,cnt=0; void add(ll u,ll v,ll w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } void dfs(ll x,ll fa) { for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { dfs(e[i].to,x); if(f[e[i].to]-e[i].w>f[x]) { g[x]=f[x]; f[x]=f[e[i].to]-e[i].w; } else { g[x]=max(g[x],f[e[i].to]-e[i].w); } } } f[x]+=a[x]; len=max(len,f[x]+g[x]); } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,u,v,w,i; cin>>n; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } dfs(1,0); cout<<len<<endl; return 0; }
\(E\) P892. 魔法水晶 \(AC\)
-
考虑建出最短路 \(DAG\) 后补全成原图并计数。
点击查看代码
const ll p=998244353; ll a[200010],cnt[200010]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,ans=1,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; cnt[a[i]]++; } for(i=1;i<=n-1&&cnt[i]!=0;i++) ans=ans*qpow((qpow(2,cnt[i-1],p)-1+p)%p,cnt[i],p)%p*qpow(2,cnt[i]*(cnt[i]-1)/2,p)%p; for(;i<=n-1;i++) if(cnt[i]!=0) ans=0; cout<<ans<<endl; return 0; }
\(F\) P893. 随机数序列
-
根据左右移位时不变的位置顺次求出其他位置。
点击查看代码
uint a[200010],t[50]; pair<uint,uint>c[200010]; uint random(uint& seed) { seed ^= (seed << 16); seed ^= (seed >> 5); seed ^= (seed << 1); return seed; } uint rev(uint x) { for(int i=31;i>=0;i--) t[i]=(x>>i)&1; for(int i=1;i<=31;i++) t[i]^=t[i-1]; for(int i=26;i>=0;i--) t[i]^=t[i+5]; for(int i=16;i<=31;i++) t[i]^=t[i-16]; x=0; for(int i=31;i>=0;i--) x=(x<<1)|t[i]; return x; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,i; uint seed; cin>>n; for(i=1;i<=n;i++) cin>>c[i].first>>c[i].second; sort(c+1,c+1+n); seed=a[c[1].first]=c[1].second; for(i=c[1].first+1;i<=200000;i++) a[i]=random(seed); for(i=2;i<=n;i++) { if(a[c[i].first]!=c[i].second) { cout<<"no"<<endl; return 0; } } seed=c[1].second; for(i=1;i<=c[1].first;i++) seed=rev(seed); cout<<seed<<endl; return 0; }
\(G\) P894. 线段树改造 \(AC\)
-
观察到循环移位 \(i \bigotimes j=(i+j+1) \bmod 4\) ,询问时的 \(+1\) 提出来最后再加。
点击查看代码
int a[200010]; struct SMT { struct SegmentTree { int len,sum,lazy; }tree[800010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(int rt) { tree[rt].sum=(tree[lson(rt)].sum+tree[rson(rt)].sum)%4; } void build(int rt,int l,int r) { tree[rt].len=r-l+1; if(l==r) { tree[rt].sum=a[l]; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushlazy(int rt,int lazy) { tree[rt].lazy=(tree[rt].lazy+lazy)%4; tree[rt].sum=(tree[rt].sum+lazy*tree[rt].len)%4; } void pushdown(int rt) { pushlazy(lson(rt),tree[rt].lazy); pushlazy(rson(rt),tree[rt].lazy); tree[rt].lazy=0; } void update(int rt,int l,int r,int x,int y,int val) { if(x<=l&&r<=y) { pushlazy(rt,val); return; } pushdown(rt); int mid=(l+r)/2; if(x<=mid) update(lson(rt),l,mid,x,y,val); if(y>mid) update(rson(rt),mid+1,r,x,y,val); pushup(rt); } int query(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) return tree[rt].sum; pushdown(rt); int mid=(l+r)/2; if(y<=mid) return query(lson(rt),l,mid,x,y); if(x>mid) return query(rson(rt),mid+1,r,x,y); return (query(lson(rt),l,mid,x,y)+query(rson(rt),mid+1,r,x,y))%4; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,pd,l,r,x,i; cin>>n>>m; for(i=1;i<=n;i++) cin>>a[i]; T.build(1,1,n); for(i=1;i<=m;i++) { cin>>pd>>l>>r; if(pd==1) { cin>>x; T.update(1,1,n,l,r,x+1); } else { cout<<(r-l+T.query(1,1,n,l,r))%4<<endl; } } return 0; }
\(H\) P895. 赌博 \(AC\)
-
题目要求找到 \((x+\frac{f(f+1)}{2}) \bmod n=0\) 的最小正整数解,观察到有用的 \(f<n\) 且 \(\sum n \le 2 \times 10^{5}\) ,枚举即可。
点击查看代码
int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,n,x,p,r,flag,i,j; cin>>t; for(j=1;j<=t;j++) { cin>>n>>x>>p; flag=0; for(i=1;i<=p&&i<=n-1;i++) { if((i*(i+1)/2%n+x)%n==0) { flag=1; cout<<"Yes"<<endl; break; } } if(flag==0) cout<<"No"<<endl; } return 0; }
\(I\) P896. 魔法水晶2
\(J\) P897. 对战
-
稍微卡卡常。
点击查看代码
const int p=998244353; int C[7010][7010],f[2][2][7010][20]; int qadd(int a,int b) { return a+b>=p?a+b-p:a+b; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,tmp,tmp1,tmp2,i,j,k; cin>>n>>m; if(1.0*m>log2(n)+5.0) { cout<<0<<endl; } else { C[0][0]=C[1][0]=C[1][1]=1; for(i=2;i<=n;i++) { C[i][0]=1; for(j=1;j<=i;j++) { C[i][j]=(C[i-1][j-1]+C[i-1][j])%p; } } for(i=0;i<=m;i++) { f[0][0][0][i]=f[0][1][0][i]=f[1][0][0][i]=f[1][1][0][i]=1; } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { for(k=1;k<=i;k++) { tmp=C[i-1][k-1]; f[0][0][i][j]=qadd(f[0][0][i][j],(1ll*f[0][1][k-1][j]*f[1][0][i-k][j]%p)*tmp%p); f[1][0][i][j]=qadd(f[1][0][i][j],(1ll*f[1][1][k-1][j-1]*f[1][0][i-k][j]%p)*tmp%p); f[0][1][i][j]=qadd(f[0][1][i][j],(1ll*f[0][1][k-1][j]*f[1][1][i-k][j-1]%p)*tmp%p); tmp1=(f[1][1][k-1][j]-f[1][1][k-1][j-1]+p)%p; tmp2=(f[1][1][i-k][j]-f[1][1][i-k][j-1]+p)%p; f[1][1][i][j]=qadd(f[1][1][i][j],((1ll*f[1][1][k-1][j]*f[1][1][i-k][j]%p-1ll*tmp1*tmp2%p+p)%p)*tmp%p); } } } cout<<(f[0][0][n][m]-f[0][0][n][m-1]+p)%p<<endl; } return 0; }
\(K\) P898. 交通网络
-
本题考验选手读题能力。
- 禁行路段数指不能被限制为单向通行甚至无法通行的边。
- 每个单程指 \(u \to v,v \to u\) 两条路径。
-
分成上行和下行两部分维护,边权转点权后线段树维护区间 \(\ge 0\) 的数的个数。
点击查看代码
struct node { int t,u,v,val,id; }q[30010]; int ans[20010],fa[20010],siz[20010],dep[20010],son[20010],top[20010],dfn[20010],cnt=0,tot=0,n; vector<int>e[20010]; bool cmp(node a,node b) { return (a.t==b.t)?(a.id<b.id):(a.t<b.t); } void add(int u,int v) { e[u].push_back(v); } void dfs1(int x) { siz[x]=1; dep[x]=dep[fa[x]]+1; for(int i=0;i<e[x].size();i++) { dfs1(e[x][i]); siz[x]+=siz[e[x][i]]; son[x]=(siz[e[x][i]]>siz[son[x]])?e[x][i]:son[x]; } } void dfs2(int x,int id) { top[x]=id; tot++; dfn[x]=tot; if(son[x]!=0) { dfs2(son[x],id); for(int i=0;i<e[x].size();i++) { if(e[x][i]!=son[x]) dfs2(e[x][i],e[x][i]); } } } struct SMT { struct SegmentTree { int len,mn[2],cnt[2],lazy[2]; }tree[80010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(int rt) { for(int i=0;i<=1;i++) { tree[rt].mn[i]=min(tree[lson(rt)].mn[i],tree[rson(rt)].mn[i]); tree[rt].cnt[i]=(tree[lson(rt)].mn[i]==tree[rt].mn[i])*tree[lson(rt)].cnt[i]+ (tree[rson(rt)].mn[i]==tree[rt].mn[i])*tree[rson(rt)].cnt[i]; } } void build(int rt,int l,int r) { tree[rt].cnt[0]=tree[rt].cnt[1]=tree[rt].len=r-l+1; tree[rt].mn[0]=tree[rt].mn[1]=0; if(l==r) return; int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); } void pushlazy(int rt,int lazy,int op) { tree[rt].mn[op]+=lazy; tree[rt].lazy[op]+=lazy; } void pushdown(int rt) { for(int i=0;i<=1;i++) { pushlazy(lson(rt),tree[rt].lazy[i],i); pushlazy(rson(rt),tree[rt].lazy[i],i); tree[rt].lazy[i]=0; } } void update(int rt,int l,int r,int x,int y,int val,int op) { if(x<=l&&r<=y) { pushlazy(rt,val,op); return; } pushdown(rt); int mid=(l+r)/2; if(x<=mid) update(lson(rt),l,mid,x,y,val,op); if(y>mid) update(rson(rt),mid+1,r,x,y,val,op); pushup(rt); } int query(int rt,int l,int r,int x,int y,int op) { if(x<=l&&r<=y) return tree[rt].len-(tree[rt].mn[op]==0)*tree[rt].cnt[op]; pushdown(rt); int mid=(l+r)/2,ans=0; if(x<=mid) ans+=query(lson(rt),l,mid,x,y,op); if(y>mid) ans+=query(rson(rt),mid+1,r,x,y,op); return ans; } }T; void update(int u,int v,int val) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { T.update(1,1,n,dfn[top[u]],dfn[u],val,0); u=fa[top[u]]; } else { T.update(1,1,n,dfn[top[v]],dfn[v],val,1); v=fa[top[v]]; } } if(dep[u]<dep[v]) T.update(1,1,n,dfn[u]+1,dfn[v],val,1); else T.update(1,1,n,dfn[v]+1,dfn[u],val,0); } int query(int u,int v) { int ans=0; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans+=T.query(1,1,n,dfn[top[u]],dfn[u],0); u=fa[top[u]]; } else { ans+=T.query(1,1,n,dfn[top[v]],dfn[v],1); v=fa[top[v]]; } } if(dep[u]<dep[v]) ans+=T.query(1,1,n,dfn[u]+1,dfn[v],1); else ans+=T.query(1,1,n,dfn[v]+1,dfn[u],0); return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int m,k,st,ed,t,u,v,p,i; cin>>n; for(i=2;i<=n;i++) { cin>>fa[i]; add(fa[i],i); } dfs1(1); dfs2(1,1); T.build(1,1,n); cin>>m; for(i=1;i<=m;i++) { cin>>st>>ed>>u>>v; q[++cnt]=(node){st,u,v,1,0}; q[++cnt]=(node){ed+1,u,v,-1,0}; } cin>>k; for(i=1;i<=k;i++) { cin>>t>>u>>v>>p; q[++cnt]=(node){t,u,v,p,i}; } sort(q+1,q+1+cnt,cmp); for(i=1;i<=cnt;i++) { if(q[i].id==0) update(q[i].u,q[i].v,q[i].val); else { u=query(q[i].u,q[i].v); v=query(q[i].v,q[i].u); if(u>q[i].val||v>q[i].val) ans[q[i].id]=-1; else ans[q[i].id]=u+v; } } for(i=1;i<=k;i++) { if(ans[i]==-1) cout<<"No"<<endl; else cout<<"Yes "<<ans[i]<<endl; } return 0; }
\(L\) P899. 疯狂的矩阵
\(M\) P900. 超级计算机
-
swap
被调用次数等价于全局逆序对数。 -
弱化版中树套树或分块的做法不适用于本题。
-
考虑将连续的一段缩成一个点进行处理,离散化维护。
点击查看代码
const ll p=998244353; ll d[200010],x[200010],y[200010]; map<ll,ll>a; map<ll,ll>::iterator it; struct BIT { ll c[200010]; ll lowbit(ll x) { return (x&(-x)); } void clear() { memset(c,0,sizeof(c)); } void add(ll n,ll x,ll val) { for(ll i=x;i<=n;i+=lowbit(i)) c[i]+=val; } ll getsum(ll x) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) ans+=c[i]; return ans; } ll query(ll l,ll r) { return l<=r?getsum(r)-getsum(l-1):0; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,l,r,ans=0,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>l>>r; if(l>r) swap(l,r); d[0]++; d[d[0]]=l; d[0]++; d[d[0]]=r; if(a.find(l)==a.end()) a[l]=l; if(a.find(r)==a.end()) a[r]=r; swap(a[l],a[r]); } sort(d+1,d+1+d[0]); d[0]=unique(d+1,d+1+d[0])-(d+1); m=0; for(it=a.begin();it!=a.end();it++) { m++; x[m]=lower_bound(d+1,d+1+d[0],it->first)-d; y[m]=lower_bound(d+1,d+1+d[0],it->second)-d; ans=(ans+max(0ll,abs(it->second-it->first)-1))%p; } for(i=1;i<=m;i++) { ans=(ans-T.query(min(x[i],y[i])+1,max(y[i],x[i])-1)+p)%p; T.add(d[0],y[i],1); } T.clear(); for(i=m;i>=1;i--) { ans=(ans-T.query(min(x[i],y[i])+1,max(y[i],x[i])-1)+T.getsum(y[i]-1)+p)%p; T.add(d[0],y[i],1); } cout<<ans<<endl; return 0; }
\(N\) P901. 线段树改造2
-
既然已经限制了 \(\bigotimes\) 具有结合律,线段树直接维护即可。
-
难点在于中缀表达式求值,建议先去写 luogu P10473 表达式计算4 。
点击查看代码
const int p=998244353; int a[200010],len; char s[60]; stack<int>s1; stack<char>s2; int val(char x) { if(x=='(') return 0; if(x=='+'||x=='-') return 1; if(x=='*') return 2; return -1; } void work() { int a=s1.top(); s1.pop(); int b=s1.top(); s1.pop(); char c=s2.top(); s2.pop(); if(c=='+') s1.push((b+a)%p); if(c=='-') s1.push((b-a+p)%p); if(c=='*') s1.push(1ll*b*a%p); } int ask(int x,int y) { int tmp=0,flag=0; for(int i=1;i<=len;i++) { if(('0'<=s[i]&&s[i]<='9')||s[i]=='a'||s[i]=='b') { if('0'<=s[i]&&s[i]<='9') tmp=(tmp*10%p+s[i]-'0')%p; else tmp=(s[i]=='a')?x:y; flag=1; if(i==len) s1.push(tmp); } else { if(flag==1) { s1.push(tmp); tmp=flag=0; } if(s[i]=='(') s2.push(s[i]); else if(s[i]==')') { while(s2.top()!='(') work(); s2.pop(); } else { while(s2.empty()==0&&val(s2.top())>=val(s[i])) work(); s2.push(s[i]); } } if(i==len) while(s2.empty()==0) work(); } return s1.top(); } struct SMT { struct SegmentTree { int sum; }tree[800010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(int rt) { tree[rt].sum=ask(tree[lson(rt)].sum,tree[rson(rt)].sum); } void build(int rt,int l,int r) { if(l==r) { tree[rt].sum=a[l]; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(int rt,int l,int r,int pos,int val) { if(l==r) { tree[rt].sum=ask(tree[rt].sum,val); return; } int mid=(l+r)/2; if(pos<=mid) update(lson(rt),l,mid,pos,val); else update(rson(rt),mid+1,r,pos,val); pushup(rt); } int query(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) return tree[rt].sum; int mid=(l+r)/2; if(y<=mid) return query(lson(rt),l,mid,x,y); if(x>mid) return query(rson(rt),mid+1,r,x,y); return ask(query(lson(rt),l,mid,x,y),query(rson(rt),mid+1,r,x,y)); } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,pd,l,r,i; cin>>(s+1)>>n>>m; len=strlen(s+1); for(i=1;i<=n;i++) cin>>a[i]; T.build(1,1,n); for(i=1;i<=m;i++) { cin>>pd>>l>>r; if(pd==1) { T.update(1,1,n,l,r); } else { cout<<T.query(1,1,n,l,r)<<endl; } } return 0; }
\(O\) P902. 水果店
总结
- 开题顺序: \(DAGBCEH\)
- \(F\) 题罚坐两小时推逆运算没推出来。
后记
-
题目中错别字较多,部分定义冗杂且陈述不清,存在歧义
-
\(H\) 数据范围分成了两部分,一半在输入格式里,一半在提升里。
-
\(O\) 因之前基本都写过遂找 \(huge\) 删了,题目背景留作存档。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18697563,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。