NOI Ag 线题选做
没做 NOI 的现实主义者在 NOI 前最现实的做法当然是做 CNOI 系列。我 NOI 居然没做几个题。
房屋老师来拜访 hzoi 了,拜谢。
我这个实力能不能 Ag 暂时不好说。那先尽量签。
[NOI2020] 美食家
好像签了这个然后剩下五个骗 \(100\) 分就能 Ag。不过这一年的 Day2 十分困难。
首先这玩意如果 \(w=1\) 看起来就是个矩阵乘法的形式,于是广义矩阵乘。然后 \(w\le 5\) 考虑拆点,把每个点拆成 \(5\) 个然后连边。然后美食节单独处理,复杂度是 \(O((5n)^3k\log T)\) 的,看上去很过不去。
然而发现我们最后只需要 \((1,1)\) 位置的值,于是只需要维护一个行向量的乘法即可,加上预处理转移矩阵的 \(2^n\) 次幂,复杂度 \(O((5n)^3\log T+(5n)^2k\log T)\),常数十分小可以过去。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int mod=998244353;
int n,m,T,k,c[60],id[60][5],cnt;
struct Mat{
long long a[310][310];
Mat(){memset(a,-0x3f,sizeof(a));}
Mat operator*(const Mat&s)const{
Mat ret;
for(int i=1;i<=cnt;i++){
for(int j=1;j<=cnt;j++){
for(int k=1;k<=cnt;k++){
ret.a[i][j]=max(ret.a[i][j],a[i][k]+s.a[k][j]);
}
}
}
return ret;
}
}P[31];
struct Vec{
long long a[310];
Vec(){memset(a,-0x3f,sizeof(a));}
Vec operator*(const Mat&s)const{
Vec ret;
for(int i=1;i<=cnt;i++){
for(int j=1;j<=cnt;j++){
ret.a[j]=max(ret.a[j],a[i]+s.a[i][j]);
}
}
return ret;
}
}ans;
struct Ques{
int t,x,y;
bool operator<(const Ques&s)const{
return t<s.t;
}
}p[210];
int main(){
scanf("%d%d%d%d",&n,&m,&T,&k);
for(int i=1;i<=n;i++)scanf("%d",&c[i]);ans.a[1]=c[1];
for(int i=1;i<=n;i++){
for(int j=0;j<5;j++)id[i][j]=++cnt;
for(int j=1;j<5;j++)P[0].a[id[i][j-1]][id[i][j]]=0;
}
for(int i=1;i<=m;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
P[0].a[id[u][w-1]][id[v][0]]=c[v];
}
for(int i=1;i<=30;i++)P[i]=P[i-1]*P[i-1];
for(int i=1;i<=k;i++)scanf("%d%d%d",&p[i].t,&p[i].x,&p[i].y);p[k+1]={T,0,0};
sort(p+1,p+k+1);
for(int i=1;i<=k+1;i++){
int tmp=p[i].t-p[i-1].t;
for(int j=30;j>=0;j--){
if((tmp>>j)&1)ans=ans*P[j];
}
ans.a[id[p[i].x][0]]+=p[i].y;
}
ans.a[1]=max(ans.a[1],-1ll);
printf("%lld\n",ans.a[1]);
return 0;
}
[NOI2020] 命运
会状态定义就是水题,但是我菜的连状态定义都出不来。
观察性质发现以某个点为下端的只有最深的上端有用,于是设 \(dp_{x,i}\) 为到 \(x\) 最深的没满足的上端点为 \(i\) 的方案数,如果都满足就是 \(0\)。那么转移考虑合并子树:
- 这条边是 \(0\):贡献显然 \(dp_{x,i}\leftarrow\sum_{\max_{a,b}=i}dp_{x,a}dp_{v,b}\)。
- 是 \(1\):同样 \(dp_{x,i}\leftarrow dp_{x,i}\times\sum_{j=0}^{dep_x}dp_{v,j}\)。
线段树合并即可。但是 pushup 不取模居然能过所有大样例。这告诉我们场上一定要拍。上次调了一下午也是 pushup 没取模。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int mod=998244353;
int n,m;
struct gra{
int v,next;
}edge[1000010];
int tot,head[500010];
void add(int u,int v){
edge[++tot].v=v;edge[tot].next=head[u];head[u]=tot;
}
int mx[500010],dep[500010];
void dfs1(int x,int f){
dep[x]=dep[f]+1;
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f)dfs1(edge[i].v,x);
}
}
struct node{
#define lson tree[rt].ls
#define rson tree[rt].rs
int ls,rs,sum,lz;
}tree[500010<<5];
int t,rt[500010];
void pushup(int rt){
tree[rt].sum=(tree[lson].sum+tree[rson].sum)%mod;
}
void pushtag(int rt,int val){
tree[rt].sum=1ll*tree[rt].sum*val%mod;
tree[rt].lz=1ll*tree[rt].lz*val%mod;
}
void pushdown(int rt){
if(tree[rt].lz!=1){
if(lson)pushtag(lson,tree[rt].lz);
if(rson)pushtag(rson,tree[rt].lz);
tree[rt].lz=1;
}
}
void update(int &rt,int L,int R,int pos,int val){
if(!rt)rt=++t,tree[rt].lz=1;
if(L==R){
tree[rt].sum=val;return;
}
pushdown(rt);
int mid=(L+R)>>1;
if(pos<=mid)update(lson,L,mid,pos,val);
else update(rson,mid+1,R,pos,val);
pushup(rt);
}
int query(int rt,int L,int R,int l,int r){
if(!rt)return 0;
if(l<=L&&R<=r)return tree[rt].sum;
pushdown(rt);
int mid=(L+R)>>1,val=0;
if(l<=mid)val=(val+query(lson,L,mid,l,r))%mod;
if(mid<r)val=(val+query(rson,mid+1,R,l,r))%mod;
return val;
}
int merge(int x,int y,int l,int r,int sumx,int sumy,int val){
if(!x&&!y)return 0;
if(!x){
pushtag(y,sumx);
return y;
}
if(!y){
pushtag(x,(val+sumy)%mod);
return x;
}
if(l==r){
tree[x].sum=(1ll*(val+sumy)%mod*tree[x].sum%mod+1ll*tree[y].sum*sumx%mod+1ll*tree[x].sum*tree[y].sum%mod)%mod;
return x;
}
pushdown(x);pushdown(y);
int mid=(l+r)>>1;
tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r,(sumx+tree[tree[x].ls].sum)%mod,(sumy+tree[tree[y].ls].sum)%mod,val);
tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid,sumx,sumy,val);
pushup(x);
return x;
}
void dfs(int x,int f){
update(rt[x],0,n,mx[x],1);
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f){
dfs(edge[i].v,x);
rt[x]=merge(rt[x],rt[edge[i].v],0,n,0,0,query(rt[edge[i].v],0,n,0,dep[x]));
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs1(1,0);
scanf("%d",&m);
for(int i=1;i<=m;i++){
int u,v;scanf("%d%d",&u,&v);
mx[v]=max(mx[v],dep[u]);
}
dfs(1,0);
printf("%d\n",query(rt[1],0,n,0,0));
return 0;
}
快踩