#1
Alpha planetary system
我们发现集合有三个,集合内的点之间没有边,于是我们希望集合内的点权值在某种意义上相等,这样任意一条边两边的点点权肯定不同。又因为边权也是 \(1,2,3\),所以我们得到可以尝试去使得同一集合内的点的点权在 \(\bmod 3\) 意义下相等。
可以发现边权为 \(3\) 的边在 \(\bmod 3\) 意义下相当于对两边的点的点权没有贡献,相当于删掉了这条边。于是我们可以通过这样的“删边”操作,得到一棵树。得到树有什么意义呢?因为叶子节点的度数为 \(1\),所以可以直接得到叶子节点到父亲的边的权值。同理,在给一个点子树内的所有边赋权后,可以通过给该点到父亲的边赋权来调整该点在 \(\bmod 3\) 意义下权值。这样子,唯一会出问题就是根节点了。
可以发现给定一个环,若给环上的边以 \(1,2,1,2\) 的顺序加权,那么只有在环是奇环时,环的起始节点会改变 \(\bmod 3\) 意义下的权值。于是我们可以找一个奇环,令根为环上一点,最后若不符合条件,则直接按照上述方法进行调整即可。
若图中没有奇环,说明这是一个二分图。对于二分图我们调整策略,同样生成一棵树,奇数层 \(\bmod 3\) 意义下权值为 \(1\),偶数层 \(b\mod 3\) 意义下权值为 \(0\),因为没有奇环,所以这个符合条件。对于根的问题,选择一个度数大于等于 \(2\) 的点即可。如果最后不满足条件,就选两条不同的临边加 \(1\) 就行。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=1e5+10;
int n,m,belong[N],head[N],tot,col_e[N],rt;
int c[N],flag,fa[N];
struct edge{
int u,v,nxt,id;
}e[N<<1];
void add(int u,int v,int id){
e[++tot].v=v;
e[tot].u=u;
e[tot].nxt=head[u];
e[tot].id=id;
head[u]=tot;
}
void add_e(int u,int v,int id){
add(u,v,id);
add(v,u,id);
}
void dfs(int u,int color){
int tot=0;
c[u]=color;
for(int i=head[u];i;i=e[i].nxt){
tot++;
int v=e[i].v;
if(c[v]==color&&!flag){
flag=i;
}
if(c[v]){
continue;
}
fa[v]=u;
dfs(v,3-color);
}
if(tot>1){
rt=u;
}
}
struct node{
int u,v,w,id;
};
bool cmp(node x,node y){
return x.u==y.u?x.id<y.id:x.u<y.u;
}
namespace odd{
int dep[N],col_p[N],round[N],vis[N];
void make_tree(int u,int father){
fa[u]=father;
dep[u]=dep[father]+1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==father){
continue;
}
if(dep[v]){
col_e[e[i].id]=3;
continue;
}
make_tree(v,u);
if(col_p[v]!=belong[v]){
col_e[e[i].id]=((belong[v]-col_p[v]+3)%3+2)%3+1;
col_p[u]+=col_e[e[i].id];
col_p[u]%=3;
}
else{
col_e[e[i].id]=3;
}
}
}
void change(int u,int val){
if(vis[u]){
return;
}
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v,id=e[i].id;
if(!round[v]||round[v]!=u){
continue;
}
col_e[id]=((col_e[id]+val)%3+2)%3+1;
change(v,3-val);
}
}
void solve(){
int u=e[flag].u,v=e[flag].v;
round[v]=u;
while(u!=v){
round[u]=fa[u];
u=fa[u];
}
make_tree(v,0);
if(col_p[v]!=belong[v]){
int delta=(belong[v]-col_p[v]+3)%3*2%3;
change(v,delta);
}
for(int i=1;i<=tot;i+=2){
write_space(e[i].u-1),write_space(e[i].v-1),write_endl(col_e[e[i].id]);
}
}
}
namespace even{
int to_fa[N],dep[N],col_p[N];
void make_tree(int u,int father){
fa[u]=father;
dep[u]=dep[father]+1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==father){
continue;
}
if(dep[v]){
col_e[e[i].id]=3;
continue;
}
to_fa[v]=e[i].id;
make_tree(v,u);
}
int col=(dep[u]%2+1)%2+1;
if(col_p[u]!=col&&u!=1){
col_e[to_fa[u]]=((col-col_p[u]+3)%3+2)%3+1;
col_p[fa[u]]+=col_e[to_fa[u]];
col_p[fa[u]]%=3;
}
else{
col_e[to_fa[u]]=3;
}
}
void solve(){
make_tree(rt,0);
if(col_p[rt]==2){
int u=0,v=0;
for(int i=head[rt];i;i=e[i].nxt){
if(!u){
u=e[i].id;
}
else if(!v){
v=e[i].id;
}
}
col_e[u]=col_e[u]%3+1;
col_e[v]=col_e[v]%3+1;
}
for(int i=1;i<=tot;i+=2){
write_space(e[i].u-1),write_space(e[i].v-1),write_endl(col_e[e[i].id]);
}
}
}
void solve(){
read(n),read(m);
for(int i=1;i<=n;i++){
char opt=getchar();
while(opt!='X'&&opt!='Y'&&opt!='Z'){
opt=getchar();
}
belong[i]=opt-'X';
}
for(int i=1;i<=m;i++){
int u,v;
read(u),read(v);
if(u>v){
swap(u,v);
}
u++,v++;
add_e(u,v,i);
}
dfs(1,1);
if(flag){
odd::solve();
}
else{
even::solve();
}
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
Fibonacci Additions
因为询问 \(a,b\) 数组是否相同,令 \(c_i=a_i-b_i\),两数组相同等价于 \(c\) 数组全为 \(0\)。因为让一个区间加上斐波那契数列很难维护,所以我们考虑差分维护这个信息。已知 \(Fib_i=Fib_{i-1}+Fib_{i-2}\),为了抵消须减去前面两个数,所以定义差分数组 \(d\),\(d_i=c_{i+2}-c_{i+1}-c_{i}\)。增加时在 \(l-2\) 处加 \(1\),在 \(r-1,r\) 处减去 \(Fib_{r-l},Fib_{r-l+1}\) 即可。因为 \(c_n\) 要等于 \(0\),所以 \(d_n=0\),同理推得 \(c\) 数组全为 \(0\) 等价于 \(d\) 数组全为 \(0\)。最后统计 \(d\) 数组中 \(0\) 的数量是否为 \(n\) 即可。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=3e5+10;
int n,q,mod,fib[N],a[N],b[N],delta[N],d[N],sum;
void update(int pos,int val){
if(!delta[pos]){
sum--;
}
delta[pos]=((delta[pos]+val)%mod+mod)%mod;
if(!delta[pos]){
sum++;
}
}
void solve(){
read(n),read(q),read(mod);
fib[1]=fib[0]=1;
for(int i=2;i<=n+5;i++){
fib[i]=(fib[i-1]+fib[i-2])%mod;
}
for(int i=1;i<=n;i++){
read(a[i]);
}
for(int i=1;i<=n;i++){
read(b[i]);
}
for(int i=1;i<=n;i++){
d[i]=((a[i]-b[i])%mod+mod)%mod;
}
for(int i=1;i<=n;i++){
delta[i]=((d[i+2]-d[i+1]-d[i])%mod+mod)%mod;
if(!delta[i]){
sum++;
}
}
while(q--){
char opt=getchar();
while(opt!='A'&&opt!='B'){
opt=getchar();
}
int type;
if(opt=='A'){
type=1;
}
else{
type=-1;
}
int l,r;
read(l),read(r);
if(l>2){
update(l-2,type);
}
if(r>1){
update(r-1,-type*fib[r-l+1]);
}
update(r,-type*fib[r-l]);
if(sum==n){
puts("YES");
}
else{
puts("NO");
}
}
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
Puzzle
先考虑只有一行的怎么做,因为两个相交线段右边的向左,左边的向右肯定不优,所以我们肯定是原序列中的顺序一一对应。转化到两行也是一样,可以发现,连线之后,不相交优于相交,所以还是从左往右一一对应,对应的两个点能同行就同行,不能再考虑不同行对应即可。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=2e5+10;
int n,a[3][N],b[3][N],q[5][N],l[5],r[5];
void solve(){
read(n);
int s1=0,s2=0;
for(int i=1;i<=2;i++){
for(int j=1;j<=n;j++){
read(a[i][j]);
s1+=a[i][j];
}
}
for(int i=1;i<=2;i++){
for(int j=1;j<=n;j++){
read(b[i][j]);
s2+=b[i][j];
}
}
if(s1!=s2){
write_endl(-1);
return;
}
int ans=0;
l[1]=l[2]=l[3]=l[4]=1;
for(int i=1;i<=n;i++){
if(a[1][i]){
q[1][++r[1]]=i;
}
if(a[2][i]){
q[2][++r[2]]=i;
}
if(b[1][i]){
q[3][++r[3]]=i;
}
if(b[2][i]){
q[4][++r[4]]=i;
}
while(l[1]<=r[1]&&l[3]<=r[3]){
ans+=abs(q[1][l[1]++]-q[3][l[3]++]);
}
while(l[2]<=r[2]&&l[4]<=r[4]){
ans+=abs(q[2][l[2]++]-q[4][l[4]++]);
}
while(l[1]<=r[1]&&l[4]<=r[4]){
ans++;
ans+=abs(q[1][l[1]++]-q[4][l[4]++]);
}
while(l[2]<=r[2]&&l[3]<=r[3]){
ans++;
ans+=abs(q[2][l[2]++]-q[3][l[3]++]);
}
}
write_endl(ans);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
Sonya and Bitwise OR
先将题目弱化一下,如果没有修改怎么办。这种区间问题,一般可以想到分治解决。一次处理经过分治中心的区间。对于一个区间,分治中心会将区间分为两部分,分别为左区间的后缀和右区间的前缀。以为题目求的是或,所以我们可以得到前缀后缀或相同的段数最多有 \(\log V\) 段。带上修改,带修分治,让人最容易想到的就是线段树。
用线段树维护,每个节点存储两个 vector<pair<int,int> >,分别表示区间前缀或和后缀或的值及对应的第一个位置,合并考虑左区间的后缀和右区间的前缀,双指针做一下求合并的答案即可。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=1e5+10;
int n,q,x,a[N];
namespace Seg_Tree{
struct node{
int ans,l,r;
vector<pii>pre,nxt;
}tr[N<<3];
int ls(int p){
return p<<1;
}
int rs(int p){
return p<<1|1;
}
void push_up(int p){
tr[p].ans=tr[ls(p)].ans+tr[rs(p)].ans;
for(int i=0,j=tr[ls(p)].nxt.size();i<tr[rs(p)].pre.size();++i){
int nxt=(i<=(int)tr[rs(p)].pre.size()-2?tr[rs(p)].pre[i+1].first:tr[p].r+1);
while(j>0&&(tr[ls(p)].nxt[j-1].second|tr[rs(p)].pre[i].second)>=x){
--j;
}
if(j!=tr[ls(p)].nxt.size()){
tr[p].ans+=1ll*(tr[ls(p)].nxt[j].first-tr[p].l+1)*(nxt-tr[rs(p)].pre[i].first);
}
}
tr[p].pre=tr[ls(p)].pre;
for(int i=0;i<tr[rs(p)].pre.size();++i){
if(tr[p].pre.back().second!=(tr[p].pre.back().second|tr[rs(p)].pre[i].second)){
tr[p].pre.pb(mp(tr[rs(p)].pre[i].first,tr[p].pre.back().second|tr[rs(p)].pre[i].second));
}
}
tr[p].nxt=tr[rs(p)].nxt;
for(int i=0;i<tr[ls(p)].nxt.size();++i){
if(tr[p].nxt.back().second!=(tr[p].nxt.back().second|tr[ls(p)].nxt[i].second)){
tr[p].nxt.pb(mp(tr[ls(p)].nxt[i].first,tr[p].nxt.back().second|tr[ls(p)].nxt[i].second));
}
}
}
void build(int p,int l,int r){
tr[p].l=l,tr[p].r=r;
if(l==r){
tr[p].ans=(a[l]>=x);
tr[p].pre.pb(mp(l,a[l]));
tr[p].nxt.pb(mp(l,a[l]));
return;
}
int mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
void update(int p,int l,int r,int pos,int val){
if(l==r){
tr[p].ans=(val>=x);
tr[p].pre[0]=tr[p].nxt[0]=mp(l,val);
return;
}
int mid=(l+r)>>1;
if(pos<=mid){
update(ls(p),l,mid,pos,val);
}
else{
update(rs(p),mid+1,r,pos,val);
}
push_up(p);
}
int query(int p,int l,int r,int q_l,int q_r){
if(l==q_l&&r==q_r){
return tr[p].ans;
}
int mid=(l+r)>>1;
if(q_r<=mid){
return query(ls(p),l,mid,q_l,q_r);
}
if(q_l>mid){
return query(rs(p),mid+1,r,q_l,q_r);
}
int ans=query(ls(p),l,mid,q_l,mid)+query(rs(p),mid+1,r,mid+1,q_r);
int j=(int)tr[ls(p)].nxt.size()-1;
while(tr[ls(p)].nxt[j].first<q_l){
j--;
}
for(int i=0,R;i<tr[rs(p)].pre.size()&&tr[rs(p)].pre[i].first<=q_r;i++){
R=(i<=(int)tr[rs(p)].pre.size()-2?tr[rs(p)].pre[i+1].first:tr[p].r+1);
R=min(R,q_r+1);
if((tr[ls(p)].nxt[j].second|tr[rs(p)].pre[i].second)<x){
continue;
}
while(j>0&&(tr[ls(p)].nxt[j-1].second|tr[rs(p)].pre[i].second)>=x){
j--;
}
ans+=1ll*(tr[ls(p)].nxt[j].first-q_l+1)*(R-tr[rs(p)].pre[i].first);
}
return ans;
}
}
void solve(){
read(n),read(q),read(x);
for(int i=1;i<=n;i++){
read(a[i]);
}
Seg_Tree::build(1,1,n);
while(q--){
int opt,x,y;
read(opt);
if(opt==1){
read(x),read(y);
Seg_Tree::update(1,1,n,x,y);
}
else{
read(x),read(y);
write_endl(Seg_Tree::query(1,1,n,x,y));
}
}
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
[HNOI2007] 神奇游乐园
整理题意,求一个权值最大的哈密顿回路。求这种东西是插头 dp 的一种非常典型的题。用四进制表示一个状态,第 \(i\) 位上为 \(0\) 表示没有插头,为 \(1\) 表示存在路径的左端点,为 \(2\) 表示存在路径的右端点。
分类讨论:
- 左侧和上方均没有插头,此时可以选择加一个下插头和右插头,也可以不管。
- 左侧有插头,上方没有,此时可以继续向右走,也可以向下转。
- 左侧无插头,上方有,此时可以继续向下走,也可以向右转。
- 左侧和上方均有插头,且均为左端点或均为右端点,可以选择将两端点合并,但是在合并的时候需要注意,为了保证路径仍然闭合,我们需要将路径另一端对应的两个端点也闭合。
- 左侧和上方均有插头,且左边的是路径右端点,上方的是路径左端点,直接合并即可。
- 左侧和上方均有插头,且左边的是路径左端点,上方的是路径右端点,根据括号序列问题,只有这对括号是相互匹配的括号时,才能直接连上,可以用哈希维护。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=110,M=1000,mod=107,inf=1e9;
int n,m,_map[N][10],q[2][M],cnt[2],h[2][mod],v[2][mod];
int get_out(int state,int pos){
return (state>>(pos*2))&3;
}
int get_in(int pos,int v){
return v*(1<<(pos*2));
}
int find_pos(int tmp,int x){
int t=x%mod;
while(h[tmp][t]!=-1&&h[tmp][t]!=x){
t=(t+1)%mod;
}
return t;
}
void ins(int tmp,int state,int val){
int t=find_pos(tmp,state);
if(h[tmp][t]==-1){
h[tmp][t]=state;
v[tmp][t]=val;
q[tmp][++cnt[tmp]]=t;
}
else{
v[tmp][t]=max(v[tmp][t],val);
}
return;
}
void solve(){
read(n),read(m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
read(_map[i][j]);
}
}
int ans=-inf;
memset(h,-1,sizeof(h));
int tmp=0;
ins(tmp,0,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=cnt[tmp];j++){
h[tmp][q[tmp][j]]<<=2;
}
for(int j=1;j<=m;j++){
tmp^=1;
cnt[tmp]=0;
memset(h[tmp],-1,sizeof(h[tmp]));
for(int k=1;k<=cnt[tmp^1];k++){
int state=h[tmp^1][q[tmp^1][k]],val=v[tmp^1][q[tmp^1][k]],x=get_out(state,j-1),y=get_out(state,j);
if(!x&&!y){
ins(tmp,state,val);
if(i<n&&j<m){
ins(tmp,state+get_in(j-1,1)+get_in(j,2),val+_map[i][j]);
}
}
else if(!x&&y){
if(i<n){
ins(tmp,state-get_in(j,y)+get_in(j-1,y),val+_map[i][j]);
}
if(j<m){
ins(tmp,state,val+_map[i][j]);
}
}
else if(x&&!y){
if(i<n){
ins(tmp,state,val+_map[i][j]);
}
if(j<m){
ins(tmp,state-get_in(j-1,x)+get_in(j,x),val+_map[i][j]);
}
}
else if(x==1&&y==1){
for(int l=j+1,sum=1;;l++){
int t=get_out(state,l);
if(t==1){
sum++;
}
else if(t==2){
sum--;
}
if(sum==0){
ins(tmp,state-get_in(j-1,x)-get_in(j,y)-get_in(l,1),val+_map[i][j]);
break;
}
}
}
else if(x==1&&y==2){
if(state==get_in(j-1,x)+get_in(j,y)){
ans=max(ans,val+_map[i][j]);
}
}
else if(x==2&&y==1){
ins(tmp,state-get_in(j-1,x)-get_in(j,y),val+_map[i][j]);
}
else{
for(int l=j-2,sum=1;;l--){
int t=get_out(state,l);
if(t==2){
sum++;
}
else if(t==1){
sum--;
}
if(sum==0){
ins(tmp,state-get_in(j-1,x)-get_in(j,y)+get_in(l,1),val+_map[i][j]);
break;
}
}
}
}
}
}
write_endl(ans);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}

浙公网安备 33010602011771号