当前位置: 首页 > news >正文

【学习笔记】NOIP暴零赛3

博弈(game)

观察到博弈过程中胜负态不会发生改变,那么求出从每个棋子出发能走的最长链,然后背包即可。

复杂度O(nm)O(nm)O(nm)

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int mod=998244353;
const int N=305;
int n,m,dp[N],in[N],g[N*N],g2[N*N],vis[N];
ll res;
string s;
queue<int>Q; 
vector<int>ve[N];
int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>m>>s;for(int i=1;i<=m;i++){int u,v;cin>>u>>v,u--,v--;if(u>v)swap(u,v);ve[v].pb(u),in[u]++;}for(int i=0;i<n;i++)if(!in[i])Q.push(i);while(Q.size()){int u=Q.front();Q.pop();for(auto v:ve[u]){if(--in[v]==0)Q.push(v);if(s[u]==s[v])dp[v]=max(dp[v],dp[u]+1),vis[v]=1;else if(s[u]!=s[v]&&!vis[u]&&!dp[u])dp[v]=max(dp[v],1);}}g[0]=g2[0]=1;for(int i=0;i<n;i++){if(s[i]=='W'){for(int j=n*n;j>=dp[i];j--){g[j]=(g[j]+g[j-dp[i]])%mod;}}else{for(int j=n*n;j>=dp[i];j--){g2[j]=(g2[j]+g2[j-dp[i]])%mod;}}}for(int i=1;i<=n*n;i++){g2[i]=(g2[i-1]+g2[i])%mod;}for(int i=1;i<=n*n;i++){res=(res+(ll)g[i]*g2[i-1])%mod;}cout<<res;
} 

排列 (perm)

神仙题。

考虑怎么转化这个性质。等价于,不能存在一个区间,里面同时存在(a,b),(b,c)(a,b),(b,c)(a,b),(b,c)而不存在(a,c)(a,c)(a,c)

由此可以想到传递闭包:每个区间,如果建一个图,就都必须具有传递性。

然后我们发现,只需要每个前缀都有传递性,每个后缀都有传递性就好了。

不要问我怎么想到的,以及证明,国内谜语人太多了,可以去骂写题解的人

也就是说,任取一个iii,设GiG_iGi为考虑前iii条边组成的图,那么GiG_iGiGiG_iGi的补图都有传递性。

有一个很神奇的结论:满足这样条件的GGG恰好有n!n!n!个,这是因为,任取一个排列ppp,如果a<ba<ba<b并且aaappp中的位置在bbb的后面,那么连边(a,b)(a,b)(a,b),这样构造出来的图一定是满足传递性的,并且这是一个双射。

可能打表反而更实际一些

然后,最无脑的想法是,因为合法的前缀只有n!n!n!个,所以状态数只有O(n!)O(n!)O(n!),也就是说可以直接把整张图的每条边存起来,然后暴力转移。不过既然我们都知道这样的GGG和一个排列ppp构成双射了,那么在ppp中转移则显得合情合理,一个合法的GGG加入一条边(a,b)(a,b)(a,b)后仍然合法,当且仅当GGG对应的排列pppaaa恰好在bbb前面的位置,加入边(a,b)(a,b)(a,b)相当于是交换(a,b)(a,b)(a,b)的位置。

复杂度O(n!×n)O(n!\times n)O(n!×n)

对不起代码咕掉了,因为感受到了出题人的恶意

另外,这题的想法也太聪明了吧。。。

考场上只写了20pts20pts20pts的暴搜,大概是因为被搞心态了所以没想到40pts40pts40pts的无脑状压dpdpdp,不过这种失分还是挺无语的

出题人你还是要有同理心啊

子段和 (seg)

放在t3t3t3其实还是比较水的。

直接用数据结构暴力维护就能做到O(n2log⁡w)O(n^2\log w)O(n2logw),可以得到70pts70pts70pts

然而没写对拍结果写炸了,警钟长鸣

接下来一步应该容易想到:对于max⁡\maxmax相同的区间,我们每次要取出min⁡\minmin最小的那一个,对于这个过程,我们可以用一个set\text{set}set来维护。考虑堆套set\text{set}set,堆里面每个元素维护区间max⁡\maxmax相同的区间的集合,用set\text{set}set维护这些区间的最小值的位置。这里有一个比较巧妙的想法,就是把堆中的元素存在一个数组里,这样每次从堆中取元素时就不用把它复制一遍了。

复杂度O(nlog⁡n)O(n\log n)O(nlogn)

后来发现题解的思路确实更加聪明。

考虑给定kkk,怎么求g(k)g(k)g(k)呢,二分答案www,令aaa为给出序列的前缀和,a0=0a_0=0a0=0,每次操作就是对一段后缀−1-11,最后要使得∀i<j,aj−ai≤w\forall i<j,a_j-a_i\le wi<j,ajaiw

直接从前往后贪心,维护一个lim\text{lim}lim表示后面的aaa经过操作后都不能超过lim\text{lim}lim。如果ai>lima_i>\text{lim}ai>lim那么就在这里执行ai−lima_i-\text{lim}ailim次操作,得到新的aaa。然后令lim:=min⁡(lim,ai+w)\text{lim}:=\min(\text{lim},a_i+w)lim:=min(lim,ai+w)

正解非常脑洞。考虑直接把所有wwwlim\text{lim}lim一起维护,设这个函数为L(w)L(w)L(w),另外维护A(w)A(w)A(w)表示目前的代价。剩下的就是用数据结构大力乱搞。

先贴一个70pts70pts70pts的代码吧。原谅我没有能力刚正解。

毕竟quack也说了这题到这里就差不多了。。。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int mod=998244353;
int n,p[200005][20],p2[200005][20],Log[200005];
ll a[200005],s[200005],Min[200005][20],Max[200005][20],K,res,inv2=(mod+1)/2;
vector<pair<ll,ll>>v;
int qmin(int l,int r){int k=Log[r-l+1];return (Min[l][k]<Min[r-(1<<k)+1][k])?p[l][k]:p[r-(1<<k)+1][k];
}
int qmax(int l,int r){int k=Log[r-l+1];return (Max[l][k]>Max[r-(1<<k)+1][k])?p2[l][k]:p2[r-(1<<k)+1][k];
}
struct node{int l,r;ll S;bool operator <(const node &a)const{return s[qmax(l,r)]-S<s[qmax(a.l,a.r)]-a.S;} 
};
priority_queue<node>q;
vector<node>vec;
ll Sum(ll l,ll r){l%=mod,r%=mod;return (r-l+1)*(l+r)%mod*inv2%mod;
}
void calc(ll &K,ll Max,ll x,ll y){if(K/x>=y){res+=Sum(Max-y+1,Max)*(x%mod)-y;res%=mod,K-=x*y;}else{res+=Sum(Max-K/x+1,Max)*(x%mod)-(K/x),res%=mod;Max-=K/x,K%=x,res+=(K%mod)*(Max%mod),res%=mod,K=0;}
}
int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n;for(int i=2;i<=n;i++)Log[i]=Log[i/2]+1;for(int i=1;i<=n;i++){cin>>a[i],s[i]=max(s[i-1]+a[i],0ll),Min[i][0]=Max[i][0]=s[i],p[i][0]=p2[i][0]=i;}for(int j=1;j<20;j++){for(int i=1;i<=n-(1<<j)+1;i++){Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]),Max[i][j]=max(Max[i][j-1],Max[i+(1<<j-1)][j-1]);p[i][j]=(Min[i][j]==Min[i][j-1])?p[i][j-1]:p[i+(1<<j-1)][j-1],p2[i][j]=(Max[i][j]==Max[i][j-1])?p2[i][j-1]:p2[i+(1<<j-1)][j-1]; }}q.push({1,n,0});cin>>K;while(q.size()){ll MAX=s[qmax(q.top().l,q.top().r)]-q.top().S,S2(inf);vec.clear();if(!MAX)break;while(q.size()&&s[qmax(q.top().l,q.top().r)]-q.top().S==MAX){int l=q.top().l,r=q.top().r;ll S=q.top().S;q.pop();S2=min(S2,s[qmin(l,r)]-S),vec.pb({l,r,S});}if(q.size())S2=min(S2,MAX-(s[qmax(q.top().l,q.top().r)]-q.top().S));for(int i=0;i<vec.size();i++){int l=vec[i].l,r=vec[i].r,p=qmin(l,r);ll S=vec[i].S;if(s[p]-S-S2==0){if(p>l)q.push({l,p-1,S+S2});if(p<r)q.push({p+1,r,S+S2});}else q.push({l,r,S+S2});}calc(K,MAX,vec.size(),S2);if(!K)break;}if(K){for(int i=1;i<=n;i++)a[i]=min(a[i],0ll);sort(a+1,a+1+n),reverse(a+1,a+1+n);for(int i=2;i<=n;i++){calc(K,a[i-1],i-1,a[i-1]-a[i]);if(!K)break;}if(K){calc(K,a[n],n,inf);}}cout<<(res+mod)%mod;
} 
http://www.lryc.cn/news/11601.html

相关文章:

  • Java JSR规范列表
  • Java必备小知识点1
  • JavaScript作用域、闭包
  • JavaScript Date(日期) 对象
  • rust过程宏 proc-macro-workshop解题-4-sorted
  • 数据结构与算法—队列
  • AcWing3416.时间显示——学习笔记
  • 贴吧手机端防删图GIF动态图制作解析
  • iOS接入Google登录
  • 【C语言】大小端字节序问题
  • Linux | 网络通信 | 序列化和反序列化的讲解与实现
  • C#的委托原理刨析and事件原理刨析和两者的比较
  • Redis学习【8】之Redis RDB持久化
  • SpringSecurity认证
  • Socket套接字
  • mysql详解之innoDB
  • 电信运营商的新尝试:探索非通信领域的发展
  • 第07章_单行函数
  • Echarts实现多柱状图重叠重叠效果
  • PHP学习笔记(一谦四益)
  • Jvm -堆对象的划分
  • 2023美赛F题讲解+数据领取
  • 【博客625】keepalived开启garp refresh的重要性
  • nginx防护规则,拦截非法字符,防止SQL注入、防XSS,nginx过滤url访问,屏蔽垃圾蜘蛛,WordPress安全代码篇
  • 【计算机网络】网络层
  • 产品经理知识体系:1.什么是互联网思维?
  • 【数据结构】单链表的接口实现(附图解和源码)
  • TikTok话题量超30亿,这款承载美好记忆的剪贴簿引发讨论
  • 了解Dubbo
  • 2023年前端面试知识点总结(JavaScript篇)