sol 7
A:
注意到只有10和结尾的1会对答案增加一次贡献
相邻两位的情况只存在4种 00 01 10 11,可以直接状态压缩
记录原数组中每个位和下一位的状态的种数,以及以0/1结尾的个数
操作时,记录当前位的变化
具体的,在该位,0会变成什么,1会变成什么
遍历4个状态,如果该位的这个状态在该次变化后变成10,累加状态数
记得算上结尾状态
code:
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f3f3f3f
#define GG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define cnot cout<<"NO"<<"\n"
#define cyes cout<<"YES"<<"\n"
#define cans cout<<ans<<"\n"
#define pb push_back
#define x0 first
#define y0 second
#define lc p<<1
#define rc p<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
#define sp(x) fixed<<setprecision(x)
#define all(v) v.begin(),v.end()
#define fr(i,st,ed) for(int i=st;i<=ed;i++)
#define ffr(i,st,ed,dt) for(int i=st;i<=ed;i+=dt)
using namespace std;
typedef pair<int,string>Pis;
typedef pair<int,int>Pii;
typedef pair<string,string>Pss;
const int N=35,mod=1e9+7,M=1e6+10;
int lowbit(int x){
return x&(-x);}
int BN4;
//00
//01
//10
//11
void solve(){
memset(B,0,sizeof(B));
int L0=0,L1=0;
int n;
cin>>n;
vector<int>a(n+1);
fr(i,1,n){
cin>>ai;
if(ai&1)L1++;
else L0++;
fr(j,0,29){
int v1=(ai>>j)&1;
int v2=(ai>>(j+1))&1;
int msk=(v2<<1)|v1;
Bjmsk++;
}
}
int Mask0=0;
int Mask1=(1<<30)-1;
int type,x;
int m;
cin>>m;
while(m--){
cin>>type>>x;
int ans=0;
if(type==1){
Mask0&=x;
Mask1&=x;
}
else if(type==2){
Mask0|=x;
Mask1|=x;
}
else{
Mask0^=x;
Mask1^=x;
}
fr(i,0,29){
if(i==0){
if(Mask0&1)ans+=L0;
if(Mask1&1)ans+=L1;
}
fr(j,0,3){
int v1=j&1;
int v2=(j>>1)&1;
int t1,t2;
if(v1==0)t1=(Mask0>>i)&1;
else t1=(Mask1>>i)&1;
if(v2==0)t2=(Mask0>>(i+1))&1;
else t2=(Mask1>>(i+1))&1;
if(t1==0&&t2==1)ans+=Bij;
}
}
cans;
}
}
signed main(){
GG;
int _t=1;
//cin>>_t;
while(_t--){
solve();
}
}
B
推导最难的数学题
P1
若在恰好喝完 m 瓶时一共中了 w 次奖,则手中剩余的钱为 n-m+cw 。 题目要求此时恰好花光,因此必须有 m-n=cw 所以当 m<n|(m-n)%c!=0时,输出0,否则中奖次数已经唯一确定:w=(m-n)/c,接下来只需统计:在长度为 m、恰有 w 个中奖的所有结果序列中,有多少个不会在第 m 瓶之前把钱花光
P2
如果你了解过卡特兰推广:
普通 Dyck 路径的步长为
+1, −1.+1,−1.
它对应的部分和满足:
Sj≥0,Sn=0.Sj≥0,Sn=0.
卡特兰数统计的是这类不跌破初始水平的路径。
所以我们希望通过"前缀和非负"解决这个问题,
把中奖记为 W,未中奖记为 L。喝完一瓶后,手中可继续购买的饮料数的变化量为:
W: c-1 L: -1
记原序列的前缀和为 S_t 。喝完前 t 瓶后,剩余饮料数为 A_t=n+S_t
一个序列合法,当且仅当:
A_t>0 (0<=t<m)
A_m=0
由 cw =m-n 可知总变化量 S_m=-n ,故第二个条件自动满足。 将序列反转并把每一项取相反数。新序列的两种步长为:
W -> 1-c
L -> 1
它们均不超过 1,总和为 n。新序列的前缀和 R_k 满足 R_k=A_{m-k} 因此原序列合法,当且仅当新序列的所有非空前缀和均为正。
对以上内容的直观理解就是:从后往前不能出现钱数为负数的情况,因为从前往后初始值有n,不好用前缀和,所以我们考虑反过来,从花完钱往开始推;
P3:
相信没多少人听过题解里面的Raney 引理,但我们可以自己推导:
如图:


code:
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f3f3f3f
#define GG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define cnot cout<<"NO"<<"\n"
#define cyes cout<<"YES"<<"\n"
#define cans cout<<ans<<"\n"
#define pb push_back
#define x0 first
#define y0 second
#define lc p<<1
#define rc p<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
#define sp(x) fixed<<setprecision(x)
#define all(v) v.begin(),v.end()
#define fr(i,st,ed) for(int i=st;i<=ed;i++)
#define ffr(i,st,ed,dt) for(int i=st;i<=ed;i+=dt)
using namespace std;
typedef pair<int,string>Pis;
typedef pair<int,int>Pii;
typedef pair<string,string>Pss;
const int N=2e6+10,mod=998244353,M=1e6+10;
int lowbit(int x){
return x&(-x);}
int qmi(int a,int b){
int res=1LL;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
int fN,invfN;
void init(){
f0=1;
fr(i,1,N-1){
fi=fi-1*i%mod;
}
invfN-1=qmi(fN-1,mod-2);
for(int i=N-2;i>=0;i--){
invfi=invfi+1*(i+1)%mod;
}
}
int C(int n,int m){
if(n<m||m<0||n<0)return 0;
return fn%mod*invfm%mod*invfn-m%mod;
}
void solve(){
int n,m,c,a,b;
cin>>n>>m>>c>>a>>b;
if((m-n)%c!=0){
cout<<0<<"\n";
return;
}
int x=(m-n)/c;
if(x>m||x<0){
cout<<0<<"\n";
return ;
}
int p=a%mod*qmi(b,mod-2)%mod;//中
int q=((b-a+mod)%mod+mod)%mod*qmi(b,mod-2)%mod;//不中
int ans=n*qmi(m,mod-2)%mod*qmi(p,x)%mod*qmi(q,m-x)%mod*C(m,x)%mod;
cans;
}
signed main(){
GG;
int _t=1;
cin>>_t;
init();
while(_t--){
solve();
}
}
F
矩阵快速幂/状态压缩优化
注意到n很小,m很大,很容易想到矩阵快速幂处理每一列多个状态对应的转移;
朴素的思路是dfs将所有可能的列状态找出来并编码,然后chang第一个状态第二个状态=1/0表示能否转移
但发现总共的状态数有1500多种,全部朴素转移会超时
考虑到 0 1 2 0 和 1 2 0 1 本质上是同一种状态,我们将这种相对顺序相同的并为一种,分别统计这样的状态有几种,此时的st状态表示该编码的状态有多少种,chang第一种状态第二种状态\[\]=num表示第一种状态转移到第二种有多少种情况;
将st*chang^m-1,得到的就是最后每一列每一种状态的值,累加既是答案
code:
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f3f3f3f
#define GG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define cnot cout<<"NO"<<"\n"
#define cyes cout<<"YES"<<"\n"
#define cans cout<<ans<<"\n"
#define pb push_back
#define x0 first
#define y0 second
#define lc p<<1
#define rc p<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
#define sp(x) fixed<<setprecision(x)
#define all(v) v.begin(),v.end()
#define fr(i,st,ed) for(int i=st;i<=ed;i++)
#define ffr(i,st,ed,dt) for(int i=st;i<=ed;i+=dt)
using namespace std;
typedef pair<int,string>Pis;
typedef pair<int,int>Pii;
typedef pair<string,string>Pss;
const int N=35,mod=998244353,M=1e6+10;
int lowbit(int x){
return x&(-x);}
vector<vector<int> >vec;
vector<vector<int>>allvec;
int n,m;
void dfs(vector<int>tmp){
if(tmp.size()==n){
allvec.pb(tmp);
return;
}
fr(i,0,2){
if(tmp.empty()||i!=tmp.back()){
tmp.pb(i);
dfs(tmp);
tmp.pop_back();
}
}
}
vector<int>ER(const vector<int>&tmp){
int id3={-1,-1,-1};
int cnt=0;
vector<int>res;
res.reserve(n);
for(int x:tmp){
if(idx==-1)idx=cnt++;
res.pb(idx);
}
return res;
}
int EC(const vector<int>&tmp){
int res=0;
for(int x:tmp){
res=res*3+x;
}
return res;
}
vector<vector<int>>chang;
vector<vector<int>> mul(vector<vector<int>> a,vector<vector<int>> b){
int n=a.size();
int m=b0.size();
int k=a0.size();
vector<vector<int>>ans(n,vector<int>(m));
fr(i,0,n-1){
fr(j,0,m-1){
fr(c,0,k-1){
ansij=(int)(((int)aic*bcj+ansij)%mod);
}
}
}
return ans;
}
vector<vector<int>> power(vector<vector<int>> m,int p){
int n=m.size();
vector<vector<int>>ans(n,vector<int>(n));
fr(i,0,n-1){
ansii=1;
}
for(;p!=0;p>>=1){
if((p&1)!=0){
ans=mul(ans,m);
}
m=mul(m,m);
}
return ans;
}
bool check(const vector<int>&a,const vector<int>&b){
fr(i,0,n-1)if(ai==bi)return 0;
return 1;
}
void solve(){
vec.clear();
allvec.clear();
chang.clear();
cin>>n>>m;
vector<int>tmp;
dfs(tmp);
/*
int sz=vec.size();
fr(i,0,sz-1){
for(int x:veci){
cout<<x<<" ";
}
cout<<"\n";
}
*/
//cout<<vec.size();
map<int,int>mp;
for(auto&v:allvec){
vector<int>nw=ER(v);
int ID=EC(nw);
if(!mp.count(ID)){
mpID=(int)vec.size();
vec.pb(nw);
}
}
int sz=(int)vec.size();
chang.resize(sz,vector<int>(sz,0));
/*
for(int i=0;i<m;i++){
for(int j=0;j<m;j++){
bool f=1;
for(int k=0;k<n;k++){
if(vecik==vecjk){
f=0;
break;
}
}
if(f)changij=1;
}
}
*/
fr(i,0,sz-1){
for(auto&v:allvec){
if(!check(veci,v))continue;
int ID=EC(ER(v));
changimp\[ID]++;
}
}
vector<int>st(sz,0);
vector<vector<int>>St;
St.pb(st);
for(auto&v:allvec){
stmp\[EC(ER(v))]++;
}
if(m==1){
cout<<allvec.size()<<"\n";
return ;
}
chang=power(chang,m-1);
//vector<vector<int>>ans=mul(St,chang);
vector<int>Ans(sz,0);
fr(i,0,sz-1){
fr(j,0,sz-1){
Ansj=(Ansj+sti*changij)%mod;
}
}
int Res=0;
for(int x:Ans)Res=(Res+x)%mod;
cout<<Res;
}
signed main(){
GG;
int _t=1;
//cin>>_t;
while(_t--){
solve();
}
}
G
二维差分/前后缀预处理
固定一个数值 x。若两个值为 x 的格子分别在 (r1,c1) 与 (r2,c2)
r1<r2,c1<c2
它们会标记矩形 r1,r2 x c1,c2。不同数值产生的矩形只需取并集,因此可以独立处理每一种数值,最后用二 维差分叠加所有矩形。
将 x 出现过的行按升序记为 x1<x2<x3<.....<xn
在同一行中,只保留 x 出现位置的最小列 mni 与最大列 mxi 即可
具体的,对于每一个元素,创建一个{行,行最小列,行最大列}的三元组,每次判断当前行是否存在,若不存在则加入当前列为最小/最大列,若存在则跟新最大列
考虑第 rt 行和第 r(t+1) 行之间的任意一条横向分界线。一个跨过该分界线的合法矩形,其左上角只能来自分界线 上方、右下角只能来自分界线下方。
Lt=min(mn1,mn2,...,mnt) Rt=max(mxt+1,mxt+2,...,mxk)
维护前/后缀,若合法,则向二维差分中跟新
code:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define endl "\n"
LL mod=998244353;
LL ksm(LL a,LL n){
LL res=1;
while(n){
if(n&1)res=res*a%mod;
n/=2;
a=a*a%mod;
}
return res%mod;
}
void solve(){
LL n,m;
cin>>n>>m;
vector<vector<LL>>a(n+1,vector<LL>(m+1));
LL k=1500;
vector<vector<pair<LL,LL>>>w(n*m+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>aij;
wa\[ij].push_back({i,j});
}
}
vector<vector<LL>>ans(n+2,vector<LL>(m+2)),pre(n+2,vector<LL>(m+2)),sum1(n+2,vector<LL>(m+2)),sum2(n+2,vector<LL>(m+2));
for(int i=1;i<=n*m;i++){
if(wi.size()<2)continue;
if(wi.size()<=k){
// cout<<i<<endl;
for(autox1,y1:wi){
for(autox2,y2:wi){
// if(x1==x2||y1==y2)continue;
// if(x1>=x2||y1>=y2)continue;
// cout<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<endl;
if(x1<x2&&y1<y2){
prex1y1++;
prex1y2+1--;
prex2+1y1--;
prex2+1y2+1++;
}
}
}
}
else{
for(int j=1;j<=n;j++){
for(int l=1;l<=m;l++)sum1jl=0,sum2jl=0;
}
for(int j=1;j<=n;j++){
for(int l=1;l<=m;l++){
sum1jl+=sum1j-1l+sum1jl-1-sum1j-1l-1;
if(ajl==i)sum1jl++;
}
}
for(int j=n;j>=1;j--){
for(int l=m;l>=1;l--){
sum2jl+=sum2j+1l+sum2jl+1-sum2j+1l+1;
if(ajl==i)sum2jl++;
}
}
for(int j=1;j<=n;j++){
for(int l=1;l<=m;l++){
if(sum1j-1l-1 && sum2jl) ansjl=1;
if(sum1jl && sum2j+1l+1) ansjl=1;
if(sum1jl-1 - sum1j-1l-1 && sum2j+1l - sum2j+1l+1) ansjl=1;
if(sum1j-1l - sum1j-1l-1 && sum2jl+1 - sum2j+1l+1) ansjl=1;
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
preij+=prei-1j+preij-1-prei-1j-1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(ansij||preij)cout<<1;
else cout<<0;
}
cout<<endl;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
LL T=1;
// build();
// cin>>T;
while(T--){
solve();
}
}
J
队友写的神秘思路
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define endl "\n"
LL mod=998244353;
LL ksm(LL a,LL n){
LL res=1;
while(n){
if(n&1)res=res*a%mod;
n/=2;
a=a*a%mod;
}
return res%mod;
}
vector<LL>fa(1e6+10),ans(1e6+10);
LL find(int x){
if(fax!=x)fax=find(fax);
return fax;
}
void union1(LL u,LL v){
LL x=find(u);
ansx=v;
fax=v;
}
void solve(){
LL n,q;
cin>>n>>q;
vector<vector<LL>>edag(n+1);
vector<LL>pa(n+1,n+1),dep(n+2),dep1(n+1);
for(int i=1;i<=n;i++)fai=i,ansi=n+1;
depn+1=1e9;
for(int i=2;i<=n;i++){
LL x;
cin>>x;
edagx.push_back(i);
edagi.push_back(x);
}
auto dfs=\&(auto&&self,int x,int fa1)->void{
if(fa1!=-1)depx=depfa1+1;
for(int y:edagx){
if(y==fa1)continue;
self(self,y,x);
}
};
dfs(dfs,1,-1);
priority_queue<tuple<LL,LL,LL>>pq;
while(q--){
int u,v;
cin>>u>>v;
pq.push({depv,u,v});
}
while(!pq.empty()){
autosum,u,v=pq.top();
//cout<<u<<" "<<v<<" "<<sum<<endl;
pq.pop();
union1(u,v);
//for(int i=1;i<=n;i++)cout<<ansi<<" ";
//cout<<endl;
}
vector<vector<LL>>edag1(n+1);
for(int i=2;i<=n;i++){
if(ansi==n+1||ansi==i)ansi=1;
//cout<<ansi<<" ";
edag1ans\[i].push_back(i);
edag1i.push_back(ansi);
}
LL ans1=0;
auto dfs1=\&(auto&&self,int x,int fa1)->void{
if(fa1!=-1)dep1x=dep1fa1+1;
ans1+=dep1x;
for(int y:edag1x){
if(y==fa1)continue;
self(self,y,x);
}
};
dfs1(dfs1,1,-1);
cout<<ans1<<endl;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
LL T=1;
// build();
// cin>>T;
while(T--){
solve();
}
}
K
叉积
签到
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f3f3f3f
#define GG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define cnot cout<<"NO"<<"\n"
#define cyes cout<<"YES"<<"\n"
#define cans cout<<ans<<"\n"
#define pb push_back
#define x0 first
#define y0 second
#define lc p<<1
#define rc p<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
#define sp(x) fixed<<setprecision(x)
#define all(v) v.begin(),v.end()
#define fr(i,st,ed) for(int i=st;i<=ed;i++)
#define ffr(i,st,ed,dt) for(int i=st;i<=ed;i+=dt)
using namespace std;
typedef pair<int,string>Pis;
typedef pair<int,int>Pii;
typedef pair<string,string>Pss;
const int N=35,mod=1e9+7,M=1e6+10;
int lowbit(int x){
return x&(-x);}
int calc(Pii a,Pii b,Pii c){
return ((b.x0-a.x0)*(c.y0-a.y0)-(b.y0-a.y0)*(c.x0-a.x0));
}
void solve(){
int n;
cin>>n;
vector<Pii>a(n+5);
fr(i,1,n)cin>>ai.x0>>ai.y0;
fr(i,2,n-1){
int x=calc(ai-1,ai,ai+1);
if(x>0)cout<<"LEFT"<<" ";
else if(x<0)cout<<"RIGHT"<<" ";
else cout<<"STRAIGHT"<<" ";
}
cout<<"\n";
}
signed main(){
GG;
int _t=1;
cin>>_t;
while(_t--){
solve();
}
}
L
博弈dp
签到
博弈模型:旗帜在网格上移动,只能往更高的格子走,因此所有可能的移动构成一个有向无环图(高度严格递增,不 可能成环)。这是一个单 token 在 DAG 上的正常博弈(normal play,无路可走者输),不是多个独立游戏的和,所 以不需要 Sprague-Grundy 理论,只需判定每个格子的胜/负状态。
胜负定义(经典):
一个格子是必胜(First),当且仅当它存在至少一个严格更高的相邻格子,且该相邻格子是必败的(把必败 局面扔给对手)
否则它是必败(Second)。特别地,局部最大值(没有更高邻居)必败
按高度递减 DP:因为每条边都由低处指向高处,所以按高度从高到低处理每个格子时,它的所有严格更高邻居都已 被计算完毕。直接迭代:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define endl "\n"
LL mod=998244353;
LL ksm(LL a,LL n){
LL res=1;
while(n){
if(n&1)res=res*a%mod;
n/=2;
a=a*a%mod;
}
return res%mod;
}
void solve(){
LL n,m;
cin>>n>>m;
vector<vector<LL>>a(n+1,vector<LL>(m+1));
priority_queue<tuple<LL,LL,LL>>pq;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>aij;
pq.push({aij,i,j});
}
}
LL dx4={0,-1,0,1},dy4={1,0,-1,0};
vector<vector<LL>>ans(n+1,vector<LL>(m+1));
while(!pq.empty()){
autosum,x,y=pq.top();
// cout<<x<<" "<<y<<endl;
pq.pop();
//ansxy=1;
for(int i=0;i<4;i++){
int x1=x+dxi,y1=y+dyi;
if(x1<1||x1>n||y1<1||y1>m)continue;
// cout<<x1<<" "<<y1<<endl;
if(ax1y1>axy){
if(ansx1y1==0)ansxy=1;
}
}
//cout<<endl;
}
LL q;
cin>>q;
while(q--){
LL r,c;
cin>>r>>c;
if(ansrc)cout<<"First"<<endl;
else cout<<"Second"<<endl;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
LL T=1;
// build();
cin>>T;
while(T--){
solve();
}
}