NOI Online 2020 #1
咕了一年的补题……
提高组
冒泡排序
令 \(x_i=\sum_{j=1}^{i-1}[a_j>a_i]\),则 \(k\) 次冒泡排序相当于将所有 \(x_i\) 变成 \(\max\{0,x_i-k\}\),树状数组维护即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline 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;
}
const int N=2e5+10;
int a[N],p[N],n;
struct bit
{
int c[N];
void init(){memset(c,0,sizeof(c));}
int query(int x,int ans=0){for(;x;x-=x&-x)ans+=c[x];return ans;}
void modify(int x,int d)
{
if(x==0)return;
for(;x<=n;x+=x&-x)c[x]+=d;
}
}t1,t2;
signed main()
{
// freopen("P6186_1.in","r",stdin);
// freopen("out.txt","w",stdout);
n=read();int m=read();
t1.init();
for(int i=1;i<=n;i++)
{
a[i]=read();
p[i]=t1.query(n)-t1.query(a[i]);
t1.modify(a[i],1);
}
// for(int i=1;i<=n;i++)printf("%d ",p[i]);
// puts("");
t1.init();t2.init();
for(int i=1;i<=n;i++)t2.modify(p[i],p[i]),t1.modify(p[i],1);
for(int i=1;i<=m;i++)
{
int pos=read();
if(pos==1)
{
// puts("haha");
int x=read();
if(a[x]<a[x+1])
{
t2.modify(p[x],-p[x]);t2.modify(p[x+1],-p[x+1]);
t1.modify(p[x],-1);t1.modify(p[x+1],-1);
swap(a[x],a[x+1]);swap(p[x],p[x+1]);
p[x+1]++;
t2.modify(p[x],p[x]);t2.modify(p[x+1],p[x+1]);
t1.modify(p[x],1);t1.modify(p[x+1],1);
}
else
{
// puts("t2.modify");
t2.modify(p[x],-p[x]);t2.modify(p[x+1],-p[x+1]);
// puts("t1.modify");
t1.modify(p[x],-1);t1.modify(p[x+1],-1);
swap(a[x],a[x+1]);swap(p[x],p[x+1]);
p[x]--;
// printf("p[x+1]:%d\n",p[x+1]);
// puts("t2.modify");
t2.modify(p[x],p[x]);t2.modify(p[x+1],p[x+1]);
// puts("t1.modify");
t1.modify(p[x],1);t1.modify(p[x+1],1);
}
// puts("HAHA");
}
else
{
// puts("hehe");
int k=read();
if(k>n){puts("0");continue;}
int cnt=t1.query(n)-t1.query(k),sum=t2.query(n)-t2.query(k);
printf("%lld\n",sum-k*cnt);
// puts("HEHE");
}
}
return 0;
}
序列
对于操作 \(2\) 连边,形成若干连通块,在和不变的情况下这些连通块内的点的点权能变成任何数。
将上面这些连通块缩点之后再对于操作 \(1\) 连边,如果最后形成的图是二分图,那么在左边右边的差不变的情况下能成为任何数,反之在左边右边差的奇偶性不变的情况下能变成任何数。
#include<bits/stdc++.h>
using namespace std;
inline 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;
}
const int N=1e5+10,M=2e5+10;
struct bjc
{
int f[N];
void init(int n){for(int i=1;i<=n;i++)f[i]=i;}
int getf(int x){return f[x]==x?x:f[x]=getf(f[x]);}
void merge(int x,int y){f[getf(y)]=getf(x);}
}t;
int head[N],ver[M],nxt[M],tot=0;
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
struct Edge
{
int u,v;
Edge(int uu,int vv){u=uu;v=vv;}
Edge(){}
}e[M];
int col[N],s[N],d[N];
bool dfs(int x,int c)
{
s[col[x]=c]+=d[x];bool f=1;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(~col[y]){if(col[y]==col[x])f=0;continue;}
if(!dfs(y,c^1))f=0;
}
return f;
}
int a[N],b[N];
void sol()
{
memset(d,0,sizeof(d));
memset(head,0,sizeof(head));tot=0;
int n=read(),m=read(),cnt=0;
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
t.init(n);
for(int i=1;i<=m;i++)
{
int tt=read(),u=read(),v=read();
if(tt==2)t.merge(u,v);
else e[++cnt]=Edge(u,v);
}
for(int i=1;i<=n;i++)d[t.getf(i)]+=b[i]-a[i];
for(int i=1;i<=cnt;i++)
{
add(t.getf(e[i].u),t.getf(e[i].v));
add(t.getf(e[i].v),t.getf(e[i].u));
}
memset(col,-1,sizeof(col));
for(int i=1;i<=n;i++)
{
if(~col[i])continue;
s[0]=s[1]=0;
bool f=dfs(i,0);
if(!f&&abs(s[0]%2)!=abs(s[1]%2)){puts("NO");return;}
if(f&&s[0]!=s[1]){puts("NO");return;}
}
puts("YES");
}
int main()
{
int T=read();
while(T--)sol();
return 0;
}
最小环
显然原图必将分出 \(\gcd(n,k)\) 个大小为 \(n/\gcd(n,k)\) 的独立的环。考虑分别处理每一个环,那么一定是尽量将大的与大的乘,可以证明这样是最优的。
对于每个询问都做一遍是 \(\mathcal O(nm)\) 的,不太行,发现 \(\gcd(n,k)\) 最多有 \(\sigma_0(n)\) 个,预处理即可。
时间复杂度 \(\mathcal O(n\sigma_0(n)+m)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline 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;
}
const int N=2e5+10;
int a[N],n,f[N];
int sol(int g)//g:环大小
{
deque<int> que;
int ans=0;
for(int j=1;j<=n/g;j++)
{
while(!que.empty())que.pop_back();
que.push_back(a[j*g]);
for(int i=j*g-1;i>=(j-1)*g+2;i--)
{
if(que.front()>que.back())ans+=a[i]*que.front(),que.push_front(a[i]);
else ans+=a[i]*que.back(),que.push_back(a[i]);
}
int x=a[(j-1)*g+1];
ans+=x*que.back()+x*que.front();
}
return ans;
}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
signed main()
{
n=read();int m=read();
for(int i=1;i<=n;i++)a[i]=read();
sort(a+1,a+n+1);
int ans=0;
for(int i=1;i<=n;i++)ans+=a[i]*a[i];
for(int i=1;i*i<=n;i++)
{
if(n%i)continue;
f[i]=sol(i);
if(i*i!=n)f[n/i]=sol(n/i);
}
for(int i=1;i<=m;i++)
{
int x=read();
if(!x)printf("%lld\n",ans);
else printf("%lld\n",f[n/gcd(n,x)]);
}
return 0;
}
普及组
买铅笔
枚举即可。
跑步
反正NOIP不考五边形数
魔法
考虑 \(k=1\) 的情况,设 \(f(x,i,j)\) 表示 \(i\sim j\) 用了 \(k\) 次魔法的最小代价,则:
\[f(1,i,j)=\min_{(u,v,w)\in E}\{f(0,i,u)+f(0,v,j)-w\}
\]
接下来考虑 \(k\ge 2\) 的情况,有:
\[f(x,i,j)=\min_{u=1}^n\{f(x-1,i,u)+f(1,u,j)\}
\]
一个经典的矩乘式子,建立矩阵跑快速幂即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline 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;
}
const int N=110;
int n;
int e[N][N],f[N][N];
struct mat
{
int a[N][N];
mat(){memset(a,0x3f,sizeof(a));}
void init(){for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)a[i][j]=e[i][j];}
mat operator * (const mat &x) const
{
mat ans;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans.a[i][j]=min(ans.a[i][j],a[i][k]+x.a[k][j]);
return ans;
}
};
struct Edge
{
int u,v,w;
Edge(int uu,int vv,int ww){u=uu;v=vv;w=ww;}
Edge(){}
}a[2510];
mat qpow(mat a,int n)
{
mat ans;
ans.init();
while(n)
{
if(n&1)ans=ans*a;
a=a*a;
n>>=1;
}
return ans;
}
signed main()
{
// freopen("P6190_3.in","r",stdin);
memset(e,0x3f,sizeof(e));
n=read();int m=read(),k=read();
for(int i=1;i<=n;i++)e[i][i]=0;
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),w=read();
e[u][v]=min(e[u][v],w);
a[i]=Edge(u,v,w);
}
for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j]=e[i][j];
for(int k=1;k<=m;k++)
{
int u=a[k].u,v=a[k].v,w=a[k].w;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],e[i][u]+e[v][j]-w);
}
mat x;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)x.a[i][j]=f[i][j];
mat ans=qpow(x,k);
printf("%lld",k==0?e[1][n]:ans.a[1][n]);
return 0;
}