A
-
加减相异的玩意,一个此类连通块内可以任意连这类边。这样一个连通块可以把欠的账堆到一个点上。
-
加减相同的操作,分连通块内有没有环、有没有奇环讨论:
- 无环:最简单,就是一棵树,一定有叶子。每次操作叶子就好了。
- 有奇环(包括自环):选出奇环上一个点,所有账扔到这个点上。这个点可以通过环自救(滑稽),不过不能改变奇偶性。
- 二部子图:同一个集合内的点相当于连了加减相异的边。
大言不惭放代码!写挂了别找我麻烦!
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long i64;
const int N=1e5+5;
struct Edg{
int u,v;
};
int a[N],b[N];
int cl[N],scl;
i64 aw[N],bw[N];
i64 sa,sb,sa1,sb1,sa0,sb0;
int bk[N],vs[N];
int f;
vector<Edg>e1;
vector<int>e2[N];
vector<int>e[N];
void dfs2(int v){
int i;
cl[v]=scl;
aw[scl]+=a[v],
bw[scl]+=b[v];
for(i=0;i<e2[v].size();i++)
if(!cl[e2[v][i]])dfs2(e2[v][i]);
}
void dfs1(int v,int p){
int i;
for(i=0;i<e[v].size();i++)
if(e[v][i]!=p){
if(!bk[e[v][i]]){
bk[e[v][i]]=-bk[v];
dfs1(e[v][i],v);
}
else{
if(bk[e[v][i]]==bk[v])f=1;
else if(!f)f=2;
}
}
}
void clc0(int v,int p){
int i;
vs[v]=1;
for(i=0;i<e[v].size();i++)
if(e[v][i]!=p&&!vs[e[v][i]])
clc0(e[v][i],v);
aw[p]+=bw[v]-aw[v];
}
void clc1(int v){
int i;
vs[v]=1;
sa+=aw[v],sb+=bw[v];
for(i=0;i<e[v].size();i++)
if(!vs[e[v][i]])clc1(e[v][i]);
}
void clc2(int v){
int i;
vs[v]=1;
if(bk[v]>0)sa1+=aw[v],sb1+=bw[v];
else sa0+=aw[v],sb0+=bw[v];
for(i=0;i<e[v].size();i++)
if(!vs[e[v][i]])clc2(e[v][i]);
}
void clr(int n){
int i;
scl=0;
vector<Edg>().swap(e1);
for(i=1;i<=n;i++){
cl[i]=0,aw[i]=0,bw[i]=0,
bk[i]=0,vs[i]=0;
vector<int>().swap(e2[i]);
vector<int>().swap(e[i]);
}
}
void rlm(){
int n,m,i,t,u,v;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=1;i<=n;i++)
scanf("%d",&b[i]);
for(i=1;i<=m;i++){
scanf("%d%d%d",&t,&u,&v);
if(t==1)
e1.push_back((Edg){u,v});
else
e2[u].push_back(v),
e2[v].push_back(u);
}
for(i=1;i<=n;i++)
if(!cl[i]){
++scl;
dfs2(i);
}
for(i=0;i<e1.size();i++)
e[cl[e1[i].u]].push_back(cl[e1[i].v]),
e[cl[e1[i].v]].push_back(cl[e1[i].u]);
for(i=1;i<=scl;i++)
if(!bk[i]){
f=0;
bk[i]=1;
dfs1(i,0);
if(f==1){
sa=sb=0;
clc1(i);
if(sa%2!=sb%2){
printf("NO\n");
clr(n);
return;
}
}
else if(f==2){
sa1=sb1=0,
sa0=sb0=0;
clc2(i);
if(sb1-sa1!=sb0-sa0){
printf("NO\n");
clr(n);
return;
}
}
else{
clc0(i,0);
if(aw[i]!=bw[i]){
printf("NO\n");
clr(n);
return;
}
}
}
printf("YES\n");
clr(n);
return;
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)rlm();
return 0;
}
B
一轮冒排能把一个数之前的比它大的数中的一个(最大的那个)弄到它后面去,从这个角度入手就很好做了。注意\(k\le 2^{31}\)
(今天下午发现CE,求我的心理阴影面积)
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long i64;
const int N=2e5+5;
int n,m;
int p[N],a[N];
int Eclc(int c){
if(c>n)c=n;
int i,j;
for(i=1;i<=n;i++)
a[i]=p[i];
for(i=1;i<=c;i++)
for(j=1;j<n;j++)
if(a[j]>a[j+1])swap(a[j],a[j+1]);
int ans=0;
for(i=1;i<n;i++)
for(j=i+1;j<=n;j++)
if(a[i]>a[j])++ans;
return ans;
}
void EasyVersion(){
int i,t,c;
for(i=1;i<=n;i++)
scanf("%d",&p[i]);
for(i=1;i<=m;i++){
scanf("%d%d",&t,&c);
if(t==1)swap(p[c],p[c+1]);
else printf("%d\n",Eclc(c));
}
}
int s[N];
int Mclc(int c){
int i,ans=0;
for(i=1;i<=n;i++)
ans+=max(0,s[i]-c);
return ans;
}
void MidVersion(){
int i,j,t,c;
for(i=1;i<=n;i++)
scanf("%d",&p[i]);
for(i=1;i<=n;i++)
for(j=1;j<i;j++)
if(p[j]>p[i])++s[i];
for(i=1;i<=m;i++){
scanf("%d%d",&t,&c);
if(t==1){
swap(s[c],s[c+1]);
if(p[c]>p[c+1])--s[c];
else ++s[c+1];
swap(p[c],p[c+1]);
}
else{
printf("%d\n",Mclc(c));
}
}
}
int btx[N];
int Pgtsm(int x){
int s=0;
for(;x;x-=x&-x)
s+=btx[x];
return s;
}
void Padd(int x){
for(;x<N;x+=x&-x)
++btx[x];
}
i64 x1[N],x2[N];
void add(int x){
if(!x){++x1[0];return;}
int y;
for(int y=x;y<N;y+=y&-y)
++x1[y],x2[y]+=x;
}
void dec(int x){
if(!x){--x1[0];return;}
for(int y=x;y<N;y+=y&-y)
--x1[y],x2[y]-=x;
}
i64 q1(int x){
i64 s=0;
for(;x;x-=x&-x)
s+=x1[x];
return s+x1[0];
}
i64 q2(int x){
i64 s=0;
for(;x;x-=x&-x)
s+=x2[x];
return s;
}
i64 Hclc(int c){
if(c>n)c=n;
return q2(n)-q2(c)-1ll*c*(n-q1(c));
}
void HardVersion(){
int i,t,c;
for(i=1;i<=n;i++)
scanf("%d",&p[i]),
a[p[i]]=i;
for(i=n;i;i--)
s[a[i]]=Pgtsm(a[i]),Padd(a[i]);
for(i=1;i<=n;i++)
add(s[i]);
for(i=1;i<=m;i++){
scanf("%d%d",&t,&c);
if(t==1){
swap(s[c],s[c+1]);
if(p[c]>p[c+1])dec(s[c]),--s[c],add(s[c]);
else dec(s[c+1]),++s[c+1],add(s[c+1]);
swap(p[c],p[c+1]);
}
else{
printf("%lld\n",Hclc(c));
}
}
}
int main()
{
freopen("bubble.in","r",stdin);
freopen("bubble.out","w",stdout);
scanf("%d%d",&n,&m);
if(n<=100&&m<=100)EasyVersion();
else if(n<=2000&&m<=2000)MidVersion();
else HardVersion();
return 0;
}
C
任何一个询问都能拆成\(\gcd(n,k)\)个环。环内和环之间的放法调整法就能证明。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long i64;
const int N=2e5+5;
int a[N];
i64 ans[N];
int y[N];
int n,m;
void EasyVersion(){
int d,i,k;
for(d=0;d<=n/2;d++){
for(i=1;i<=n;i++)y[i]=i;
ans[d]=0;
do{
i64 t=0;
for(i=1;i<=n;i++)t+=1ll*a[y[i]]*a[y[(i+d-1)%n+1]];
ans[d]=max(ans[d],t);
}while(next_permutation(y+1,y+n+1));
}
for(i=1;i<=m;i++)
scanf("%d",&k),
printf("%lld\n",ans[k]);
}
int gcd(int n,int m){
return m?gcd(m,n%m):n;
}
void HardVersion(){
int d,l,i,k;
for(d=1;d<=n;d++)
if(n%d==0){
l=n/d;
int ll=l,rr=1;
for(i=1;i<=l;i++)
if(i&1)y[rr++]=i;
else y[ll--]=i;
y[0]=y[l];
i64 t=0;
int x;
for(x=0;x<n;x+=l)
for(i=1;i<=l;i++)
t+=1ll*a[y[i]+x]*a[y[i-1]+x];
ans[d]=t;
}
for(i=1;i<=m;i++)
scanf("%d",&k),
printf("%lld\n",ans[gcd(n,k)]);
}
int main()
{
freopen("ring.in","r",stdin);
freopen("ring.out","w",stdout);
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
HardVersion();
return 0;
}