NOIP 模拟 18
最近老是犯唐,这次也是。
T1 图
容易得到暴力代码:
namespace s1{
bool sta[MAXN*MAXN];
bool S[MAXN],T[MAXN];
string s;
int ans;
int main(){
cin>>n>>m;
for(int i=1;i<=m;++i){
cin>>s;
memset(S,0,sizeof(bool)*(n+5));
memset(T,0,sizeof(bool)*(n+5));
for(int j=0;j<n;++j)
switch(s[j]){
case '0': break;
case '1': T[j+1]=1;break;
case '2': S[j+1]=1;break;
default: S[j+1]=T[j+1]=1;break;
}
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
if((S[i]&&T[j])||(S[j]&&T[i]))
sta[i*(n-1)+j]^=1;
}
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
if(sta[i*(n-1)+j])
++ans;
cout<<ans<<'\n';
return 0;
}
}
其实就是模拟题面的过程。我们发现 \(n\le10^4\),是可以通过 \(O(n^2)\) 的算法的。暴力程序真正的瓶颈在于每次都 \(n^2\) 枚举而外面又套了一个 \(m\),复杂度变成 \(O(mn^2)\),无法通过。
发现每次的 \(n^2\) 枚举的内部都只是简单的位运算 + 异或。不难想到 \(\textbf{bitset}\) 处理。具体地,将所有的 bool 全换成 bitset,然后:
- 若 \(S_i=1\),则 \(\mathit{ans}_i\gets\mathit{ans}_i\oplus T_i\);
- 若 \(T_i=1\),则 \(\mathit{ans}_i\gets\mathit{ans}_i\oplus S_i\);
- 若 \(S_i=1\land T_i=1\),这时候会算重一次,只需 \(\mathit{ans}_i\gets\mathit{ans_i}\oplus(S_i\;\&\;T_i)\)。
然后就没了。本质上就是把 \(n^2\) 的枚举用 bitset 一步到位了。
for(int i=1,s,t;i<=n;++i){
s=S[i],t=T[i];
S[i]=T[i]=0;
if(s) mp[i]^=T;
if(t) mp[i]^=S;
if(s&&t) mp[i]^=S&T;
}
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
if(mp[i][j])
++ans;
cout<<ans<<'\n';
return 0;
评价:bitset 使用不够熟练。考场上以为是什么神仙性质题,关键是还真推出来了只有 \(1\) 和 \(2\) 时的性质,于是就一直在错误的方向上摸索。
T2 序列
首先必须想到的一步转化:将所有操作转化为将总答案乘上一个数。
那么对于操作一,转化为贡献 \(\dfrac y{a_x}\);对于操作二,转化为贡献 \(\dfrac{a_x+y}{a_x}\);对于操作三,直接就是贡献 \(y\)。
然而这样做会有一个问题,比如操作一,一旦之前有对 \(a_x\) 操作,那么 \(a_x\) 的值就会改变。也就是说,我们对一次操作的贡献无法静态算出。操作二也同理。
其实操作一导致的问题是好解决的,只需要取对 \(\forall a_x\) 的所有操作一中 \(y\) 的最大值,然后操作一就可以转化为操作二:把 \(a_x\) 修改为 \(y\) 相当于加上 \(y-a_x\)。但是,操作二造成的影响我们似乎真的不好处理。
考场上想到这就止步了。
但实际上,有一个显然的性质:对 \(\forall a_x\) 的加法操作中,我们肯定先操作 \(y\) 大的,然后再操作 \(y\) 小的。于是,当我们对 \(y_i\) 计算贡献时,此时的 \(a_x\) 实际上已经变为 \(a_x+\sum_{y_j>y_i}y_j\)。这只需要我们对于任意点的操作二进行从大到小排序,然后操作二的贡献我们就可以计算了。
于是我们将所有的操作都转化为了对总答案乘上一个数。从大到小排序,然后挨个计算并输出即可。
#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
#define int long long
using namespace std;
char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){
int x=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x;
}
template<typename T>
void write(T x,char sf='\n'){
if(x<0)putchar('-'),x=~x+1;
int top=0;
do str[top++]=x%10,x/=10;while(x);
while(top)putchar(str[--top]+48);
if(sf^'#')putchar(sf);
}
constexpr int MAXN=1e5+5,MOD=1e9+7;
int n,m,a[MAXN],ans=1;
vector<int>pls[MAXN];
int chg[MAXN];
vector<pair<double,int>>f;
int max(int a,int b){
return a>b?a:b;
}
int power(int a,int b){
int res=1;
for(;b;a=a*a%MOD,b>>=1)if(b&1)res=res*a%MOD;
return res;
}
signed main(){
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1,t,x,y;i<=m;++i){
t=read(),x=read(),y=read();
switch(t){
case 1: chg[x]=max(chg[x],y);break;
case 2: pls[x].emplace_back(y);break;
default:f.emplace_back(y,y);
}
}
for(int i=1,t;i<=n;++i){
if(a[i]<chg[i]) pls[i].emplace_back(chg[i]-a[i]);
sort(pls[i].begin(),pls[i].end(),greater<>());
t=a[i];
for(auto x:pls[i]){
f.emplace_back(1.0*(t+x)/t,(t+x)*power(t,MOD-2)%MOD);
t+=x;
}
}
sort(f.begin(),f.end(),greater<>());
for(int i=1;i<=n;++i) ans=ans*a[i]%MOD;
for(auto x:f){
write(ans,' ');
ans=ans*x.second%MOD;
}
for(int i=f.size();i<=m;++i) write(ans,' ');
return fw,0;
}
T3 树
什么神仙题。
T4 字符串
有一道基本相同的题目:CF610E。
题目中 “插入字符” 操作不好维护,观察到 \(t\) 是一个排列,所以每个字母在 \(t\) 中仅会出现一次。故有引理:
任意字符 \(s_i\) 和 \(s_{i+1}\) 在一个循环节中,当且仅当 \(s_i\) 和 \(s_{i+1}\) 在 \(t\) 中相邻。
所以将每个字符按照在 \(t\) 中的位置编号,则原串转化为一个数字串,我们要求的就是数字串的逆序对数。区间修改显然线段树维护,在每个线段树节点上设 \(\mathit{sm}_{i,j}\) 为 \([l,r]\) 中每一对相邻的字符中 \((\forall k)~s_k=i\land s_{k+1}=j\) 的数有多少个。复杂度 \(O(mk^2\log n)\)。
#include<bits/stdc++.h>
#define lp p<<1
#define rp p<<1|1
#define mid ((s+t)>>1)
using namespace std;
constexpr int MAXN=2e5+5;
int n,m,k,tmp[10][10];
string S;
struct SegTree{
int lc,rc,sm[10][10],lazy;
SegTree(){
lc=rc=lazy=0;
memset(sm,0,sizeof(sm));
}
int*operator[](const int&x){
return sm[x];
}
}st[MAXN<<2];
void pushup(int p){
for(int i=0;i<k;++i)
for(int j=0;j<k;++j)
st[p][i][j]=st[lp][i][j]+st[rp][i][j];
st[p].lc=st[lp].lc,st[p].rc=st[rp].rc;
++st[p][st[lp].rc][st[rp].lc];
}
void build(int s,int t,int p){
if(s==t){
st[p].lc=st[p].rc=S[s]-'a';
return;
}
build(s,mid,lp),build(mid+1,t,rp);
pushup(p);
}
void pushlazy(int p,int c){
for(int i=0;i<k;++i)
for(int j=0;j<k;++j)
tmp[i][j]=st[p][i][j];
for(int i=0;i<k;++i)
for(int j=0;j<k;++j)
st[p][(i+c)%k][(j+c)%k]=tmp[i][j],tmp[i][j]=0;
st[p].lc=(st[p].lc+c)%k;
st[p].rc=(st[p].rc+c)%k;
st[p].lazy=(st[p].lazy+c)%k;
}
void pushdown(int p){
if(!st[p].lazy) return;
pushlazy(lp,st[p].lazy);
pushlazy(rp,st[p].lazy);
st[p].lazy=0;
}
void mdf(int l,int r,int c,int s=1,int t=n,int p=1){
if(l<=s&&t<=r) return pushlazy(p,c),void();
pushdown(p);
if(l<=mid) mdf(l,r,c,s,mid,lp);
if(mid<r) mdf(l,r,c,mid+1,t,rp);
pushup(p);
}
SegTree ask(int l,int r,int s=1,int t=n,int p=1){
if(l<=s&&t<=r) return st[p];
pushdown(p);
if(l>mid) return ask(l,r,mid+1,t,rp);
if(mid>=r) return ask(l,r,s,mid,lp);
SegTree res1=ask(l,r,s,mid,lp),res2=ask(l,r,mid+1,t,rp),res;
for(int i=0;i<k;++i)
for(int j=0;j<k;++j)
res[i][j]=res1[i][j]+res2[i][j];
res.lc=res1.lc,res.rc=res2.rc;
++res[res1.rc][res2.lc];
return res;
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>m>>k>>S;
S=' '+S;
build(1,n,1);
while(m--){
int op,l,r;
cin>>op>>l>>r;
switch(op){
case 1:{
int c;
cin>>c;
mdf(l,r,c);
break;
}default:{
string s;
cin>>s;
int ans=0;
SegTree res=ask(l,r);
for(int i=0;i<k;++i)
for(int j=0;j<=i;++j)
ans+=res[s[i]-'a'][s[j]-'a'];
cout<<ans+1<<'\n';
break;
}
}
}
return 0;
}
标签:res,NOIP,mathit,int,18,MAXN,ans,操作,模拟
From: https://www.cnblogs.com/laoshan-plus/p/18561369