NOIP2023补题
T1
赛时100pts:
操作相当于冒泡排序,那么直接分解出26字母比较即可
时间复杂度 \(O(26n^2)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define ll long long
#define endl '\n'
const int N=3e3+5;
const int M=1e6+5;
inl int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
inl void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
inl void writei(int x){write(x);putchar(' ');}
int n,m,tong[N],ans[N];
struct node{
int type,cnt;
friend bool operator<(node a,node b){return a.type<b.type;}
};
vector<node>v[N];
char s[N];
inl bool comp(int a,int b){
for(int i=0,j=v[b].size()-1;i<v[a].size()&&~j;i++,j--){
if(v[a][i].type==v[b][j].type&&v[a][i].cnt==v[b][j].cnt)continue;
return (v[a][i].type^v[b][j].type)?v[a][i].type<v[b][j].type:0;
}
return 0;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=26;j++)tong[j]=0;
for(int j=1;j<=m;j++)cin>>s[j],tong[s[j]-'a'+1]++;
for(int j=1;j<=26;j++){
if(tong[j])v[i].push_back((node){j,tong[j]});
}
sort(v[i].begin(),v[i].end());
}
for(int i=1;i<=n;i++){
int flag=1;
for(int j=1;j<=n;j++){
if(i==j)continue;
flag&=comp(i,j);
if(!flag)break;
}
ans[i]=flag;
}
for(int i=1;i<=n;i++)cout<<ans[i];cout<<endl;
return 0;
}
T2
赛时60pts代码:
数据点分治。代码依托答辩。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define ll long long
#define endl '\n'
const int N=1e5+5;
const int M=1e6+5;
const int inf=0x7fffffff;
inl int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
inl void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
inl void writel(int x){write(x);putchar('\n');}
inl void writei(int x){write(x);putchar(' ');}
int c,t,n,m;
namespace sub1{
int a[N],b[N],res;
struct qus{
char v;
int x,y;
}q[N];
inl void dfs(int k){
if(k>n){
int ans=0,flag=1;
for(int i=1;i<=n;i++)b[i]=a[i],ans+=(!~a[i]);
for(int i=1;i<=m;i++){
switch(q[i].v){
case 'T':
b[q[i].x]=1;
break;
case 'F':
b[q[i].x]=0;
break;
case 'U':
b[q[i].x]=-1;
break;
case '+':
b[q[i].x]=b[q[i].y];
break;
default:
if(~b[q[i].y])b[q[i].x]=b[q[i].y]^1;
else b[q[i].x]=b[q[i].y];
break;
}
}
for(int i=1;i<=n;i++)flag&=(a[i]==b[i]);
if(flag)res=min(res,ans);
return;
}
a[k]=1;dfs(k+1);
a[k]=0;dfs(k+1);
a[k]=-1;dfs(k+1);
}
inl void solve(){
while(t--){
n=read();m=read();res=inf;
for(int i=1;i<=m;i++){
scanf(" %c",&q[i].v);
if(q[i].v=='T'||q[i].v=='F'||q[i].v=='U')q[i].x=read();
else q[i].x=read(),q[i].y=read();
}
dfs(1);
writel(res);
}
}
}
namespace sub2{
char v;
int x,y,a[N];
inl void solve(){
while(t--){
memset(a,0,sizeof a);
n=read();m=read();int ans=0;
for(int i=1;i<=m;i++){
scanf(" %c",&v);
switch(v){
case 'T':
x=read();
a[x]=1;
break;
case 'F':
x=read();
a[x]=0;
break;
case 'U':
x=read();
a[x]=-1;
break;
case '+':
x=read();y=read();
a[x]=a[y];
break;
default:
x=read();y=read();
if(~a[y])a[x]=a[y]^1;
else a[x]=a[y];
break;
}
}
for(int i=1;i<=n;i++)ans+=(!~a[i]);
writel(ans);
}
}
}
namespace sub3{
char op;
int x,y,q[N],h,tt,vis[N];
struct node{
int pos,flag;
}f[N];
vector<int>v[N];
inl void solve(){
while(t--){
n=read();m=read();
int ans=0;
for(int i=1;i<=n;i++)v[i].clear();
for(int i=1;i<=n;i++)f[i]={i,1};
memset(vis,0,sizeof vis);
h=0,tt=1;
for(int i=1;i<=m;i++){
scanf(" %c",&op);
switch(op){
case 'T':
x=read();
f[x]={0,1};
break;
case 'F':
x=read();
f[x]={0,0};
break;
case 'U':
x=read();
f[x]={0,-1};
break;
case '+':
x=read();y=read();
f[x]=f[y];
break;
default:
x=read();y=read();
if(!f[y].pos){
if(~f[y].flag)f[x]={0,f[y].flag^1};
else f[x]={0,f[y].flag};
}else
f[x]=f[y];f[x].flag*=-1;
break;
}
}
for(int i=1;i<=n;i++){
// cout<<'/'<<f[i].pos<<' '<<f[i].flag<<endl;
if(f[i].pos){
if(i==f[i].pos){
if(!~f[i].flag)q[++h]=i,ans++,vis[i]=1;
}else v[f[i].pos].push_back(i);
}else if(!f[i].pos){
if(!~f[i].flag)q[++h]=i,ans++,vis[i]=1;
}
}
while(h>=tt){
int x=q[tt++];
for(auto i:v[x])if(!vis[i])q[++h]=i,ans++,vis[i]=1;
}
writel(ans);
}
}
}
signed main(){
c=read();t=read();
if(c==1||c==2)sub1::solve();
if(c==3||c==4)sub2::solve();
if(c>=5)sub3::solve();
return 0;
}
正解:
想到了求最终位置没想到扩展域并查集,鉴定为菜
首先可以对每个点求出他的最终答案是那个位置赋值过来的,那么这两个点初值一定相等,连边,可以用扩展域并查集维护联通块
由于可能反向赋值,用扩展域并查集 两层分别代表 \(T\) 和 \(F\)
对于一个联通块 当存在一个点被赋值成 \(U\) 或者存在一个点 \(T\) 和 \(F\) 联通 该联通块就要全赋值为 \(U\)
时间复杂度 \(O(Tn\log n)\)(这破并查集调了好久/fn
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define endl '\n'
#define int ll
#define gc getchar
#define pc putchar
const int N=1e5+5;
const int M=1e7+5;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int n,m,c,t,fa[N<<1],x,y,fx,fy,vis[N<<1];
char v;
struct node{
int pos,flag;
}f[N];
inl int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
signed main(){
c=read();t=read();
while(t--){
n=read();m=read();int ans=0;
for(int i=1;i<=(n<<1);i++)fa[i]=i;
for(int i=1;i<=n;i++)f[i]={i,1};
memset(vis,0,sizeof vis);
while(m--){
scanf(" %c",&v);
switch(v){
case 'T':
x=read();
f[x]={0,1};
break;
case 'F':
x=read();
f[x]={0,0};
break;
case 'U':
x=read();
f[x]={0,-1};
break;
case '+':
x=read();y=read();
f[x]=f[y];
break;
default:
x=read();y=read();
if(!f[y].pos){
if(~f[y].flag)f[x]={0,f[y].flag^1};
else f[x]={0,f[y].flag};
}else
f[x]={f[y].pos,f[y].flag^1};
break;
}
}
for(int i=1;i<=n;i++){
if(!f[i].pos)continue;
fa[find(i)]=find(f[i].pos+(f[i].flag^1)*n);
fa[find(i+n)]=find(f[i].pos+f[i].flag*n);
}
for(int i=1;i<=n;i++){
if(!f[i].pos){
vis[find(i)]=vis[find(i+n)]=!~f[i].flag;
continue;
}
}
for(int i=1;i<=n;i++){
ans+=vis[find(i)]||find(i)==find(i+n);
}
cout<<ans<<endl;
}
return 0;
}
T4
赛时8pts:
似乎咋写都能拿到这8分特殊性质,代码没调出来写的很丑就不放了
正解:
为啥我之前写过一道基本一样线段树优化dp的题还是调不出来啊/fn
发现要跑一定是跑连续段,那么设 \(f_i\) 表示第 \(i\) 天不跑的最优答案
显然有转移:\(f_i=max(f_j+g(j+1,i-1)-(i-j-1)\times d)\)
g为 \([j+1,i-1]\)区间包含的线段的价值和 相当于令 \([j+1,i-1]\) 一直跑
直接转移优化是 \(O(n^2)\) 或 \(O(nm)\) 的
发现最优情况一定是从某个线段开头跑 从某个线段结尾结束
那么 \(i\) 的取值降到 \(O(m)\) 级别 总复杂度 \(O(m^2)\)
转化一下 \(f_i-i\times d=max((f_j-j\times d)+g(j+1,i-1)+d)\)
显然可以预处理 \(i-1\) 位置结尾的所有线段 然后把贡献累加到线段开头以前的点(区间加)
求 \(max((f_j-j\times d)+g(j+1,i-1)+d)\) 为区间求 \(max\) 显然可以线段树维护
对于区间不选情况 应该直接继承上一个位置的ans 考试忘了这个没调出来 补题又调一下午/fn
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define endl '\n'
#define int ll
#define gc getchar
#define pc putchar
const int N=2e5+5;
const int M=1e7+5;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int c,t,n,m,k,d,l[N],r[N],a[N],lsh[N];
vector<int>v[N];
struct segment_tree{
int s[N<<2],tag[N<<2];
#define mid (l+r>>1)
#define ls k<<1
#define rs k<<1|1
inl void clear(int k,int l,int r){
s[k]=tag[k]=0;
if(l==r)return;
clear(ls,l,mid);clear(rs,mid+1,r);
}
inl void pushup(int k){s[k]=max(s[ls],s[rs]);}
inl void adt(int k,int v){s[k]+=v;tag[k]+=v;}
inl void pushdown(int k){
if(!tag[k])return;
adt(ls,tag[k]);adt(rs,tag[k]);
tag[k]=0;
}
inl void modify(int k,int l,int r,int x,int y,int v){
if(x<=l&&r<=y)return adt(k,v);
pushdown(k);
if(x<=mid)modify(ls,l,mid,x,y,v);
if(y>mid)modify(rs,mid+1,r,x,y,v);
pushup(k);
}
inl int query(int k,int l,int r,int x,int y){
if(x<=l&&r<=y)return s[k];
int ans=-inf;pushdown(k);
if(x<=mid)ans=max(ans,query(ls,l,mid,x,y));
if(y>mid)ans=max(ans,query(rs,mid+1,r,x,y));
return ans;
}
}SGT;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
c=read();t=read();
while(t--){
n=read();m=read();k=read();d=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
l[i]=lsh[(i<<1)-1]=x-y;
r[i]=lsh[i<<1]=x+1;
a[i]=read();
}
sort(lsh+1,lsh+(m<<1)+1);
int p=unique(lsh+1,lsh+(m<<1)+1)-lsh-1;
for(int i=1;i<=m;i++){
l[i]=lower_bound(lsh+1,lsh+p+1,l[i])-lsh;
r[i]=lower_bound(lsh+1,lsh+p+1,r[i])-lsh;
v[r[i]].push_back(i);
}
int lim=1,ans=0;
SGT.clear(1,1,p);
SGT.modify(1,1,p,1,1,lsh[1]*d);
for(int i=2;i<=p;i++){
for(auto j:v[i])SGT.modify(1,1,p,1,l[j],a[j]);
while(lsh[i]-lsh[lim]-1>k)lim++;
ans=max(ans,SGT.query(1,1,p,lim,i-1)-(lsh[i]-1)*d);
SGT.modify(1,1,p,i,i,ans+lsh[i]*d);
}
cout<<ans<<endl;
for(int i=1;i<=m;i++)v[r[i]].clear();
}
return 0;
}

浙公网安备 33010602011771号