191004Day2---金华

T1 异或

给你\(n\)个形如\([l_i,r_i]\)区间,设\(S_i=\bigcup_{j=1}^{i}[l_j,r_j]\),对于所有\(i\in[1,n]\),求\(\sum_{x,y\in S_i,x\leq y}[x\) \(xor\) \(y\)在二进制下一的个数有奇数个\(]\)。
\(n\leq10^5,0\leq l\leq r\leq2^{31}-1\)

sol:

首先发现维护的内容是区间并,想到有一个绝配的数据结构叫线段树。因为\(l\)和\(r\)的范围可以到\(2^{31}-1\),所以考虑动态开点或离散化。然后因为两个数异或后\(1\)的个数的奇偶性,与原来两个数\(1\)的个数之和的奇偶性相同,令\(X\)为区间内有几个含奇数个\(1\)的数,\(Y\)为有几个含偶数个\(1\)的数,所以只要分别处理出\(X\)和\(Y\),\(X\times Y\)即是答案。
方法1:动态开点线段树。这时会有一个非常良好的性质,就是对于一个线段树上的区间\([l,r]\),\(X\)的个数与\(Y\)的个数相等,于是一个区间的贡献可以在\(O(1)\)的时间内计算。
方法2:离散化+线段树。离散后可以不用动态开点,节省了空间,但同时失去了上一种做法的性质。现在继续考虑如何计算\(X\)和\(Y\)。我们把\(X_{[l,r]}\)拆成\(X_{[0,r]}-X_{[0,l-1]}\),对于\(Y\)同理。对于区间\([0,a]\),若\(a\)是奇数,则\(X_{[0,a]}=\frac{a}{2}+1\),若\(a\)为偶数,则还要判断\(a\)是否对\(X\)造成贡献。
方法3:离散化+set。常数有点大。Orz xuyuan
方法1代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x;
}
const int N=100005,maxn=(1<<31)-1;
int n,cnt=1;
struct node{int l,r,data1,data2;bool vis;} st[N<<5];
void inst(int x,int l,int r,int lb,int rb){
    if(lb==rb){
        int num=0;
        for(int i=lb;i>0;i>>=1)
            if(i&1) num++;
        if(num&1) st[x].data1=1;
        else st[x].data1=0;
        st[x].data2=1-st[x].data1;
        st[x].vis=1;return;
    }
    if(l<=lb&&r>=rb){
        st[x].data2=st[x].data1=(rb-lb+1)>>1;
        st[x].vis=1;return;
    }
    int mid=(1ll*lb+rb)>>1;
    if(st[x].l==0) st[x].l=++cnt;
    if(l<=mid&&!st[st[x].l].vis) inst(st[x].l,l,r,lb,mid);
    if(st[x].r==0) st[x].r=++cnt;
    if(r>mid&&!st[st[x].r].vis) inst(st[x].r,l,r,mid+1,rb);
    st[x].data1=st[st[x].l].data1+st[st[x].r].data1;
    st[x].data2=st[st[x].l].data2+st[st[x].r].data2;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        int x=read(),y=read();
        inst(1,x,y,0,maxn);
        printf("%lld\n",1ll*st[1].data1*st[1].data2);
    }
    return 0;
}

T2 图论

给一个\(n\)个点的无向连通图,求解任意一种添加边的方式,使图变成一个连通的欧拉回路。
\(2\leq n\leq2000\)

sol:

思路1:首先对于那些奇数度的点,要想办法把它们变成偶数度。考虑建一张反图,在图上增广,具体就是对于两个奇数度的点,在反图上找到任一条路径,反转边的存在情况(原来选的边不选,原来不选的边选)。结束后整个图的点都为偶数度了,但不保证连通。当连通块个数大于两个时,只要把它们串成一个环即可;当连通块个数等于两个时,可以考虑在某一块中删除一条已有,或增加一条没有,且可选可不选的边,再把那条边的两个端点连向另一块中的某个点。
思路2:对于一个奇数个点的图,完全图显然是一个解;对于一个偶数个点的图,考虑在完全图中删除\(n/2\)条边,做法与上一种类似。
思路1代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x;
}
const int N=1005;
int n,deg[N],st[N],ve[N],rev[N][N],ch[N][N],cnt,num;
char p[N][N];
bool vis[N],im;
void dfs(int x,int y,int la){
    vis[x]=1;
    for(int i=1;i<=n;i++){
        if(!rev[x][i]||i==la) continue;
        if(i==y){ch[x][i]^=1;ch[i][x]^=1;im=1;return;}
        if(vis[i]) continue;
        ch[x][i]^=1;ch[i][x]^=1;
        dfs(i,y,x);
        if(im) return;
        ch[x][i]^=1;ch[i][x]^=1;
    }
}
void print(){
    puts("Yes");
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            if(p[i][j]=='1'||ch[i][j]||ch[j][i]) putchar('1');
            else putchar('0');
        puts("");
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",p[i]+1);
        for(int j=1;j<=n;j++){
            if(p[i][j]=='1') deg[j]++;
            if(p[i][j]=='1'||i==j) rev[i][j]=0;
            else rev[i][j]=1;
        }
    }
    for(int i=1;i<=n;i++){
        if(deg[i]&1) st[++cnt]=i;
    }
    if(cnt&1){puts("No");return 0;}
    for(int i=1;i<=cnt;i+=2){
        memset(vis,0,sizeof(vis));
        im=0;dfs(st[i],st[i+1],0);
        if(!im){puts("No");return 0;}
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(ch[i][j]) deg[j]++;
        }
    for(int i=1;i<=n;i++)
        if(deg[i]==0) ve[++num]=i;
    if(num==1){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++){
                if(ch[i][j]){
                    ch[i][j]=ch[j][i]=0;
                    ch[ve[1]][i]=ch[i][ve[1]]=1;
                    ch[ve[1]][j]=ch[j][ve[1]]=1;
                    print();return 0;
                }
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++){
                if(ch[i][j]==0&&i!=j&&p[i][j]=='0'){
                    ch[i][j]=ch[j][i]=1;
                    ch[ve[1]][i]=ch[i][ve[1]]=1;
                    ch[ve[1]][j]=ch[j][ve[1]]=1;
                    print();return 0;
                }
            }
        puts("No");return 0;
    }
    if(num==n){
        for(int i=1;i<n;i++)
            ch[i][i+1]=ch[i+1][i]=1;
        ch[n][1]=ch[1][n]=1;
        print();return 0;
    }
    int x,y;
    for(int i=1;i<=n;i++)
        if(deg[i]>0){
            x=i;
            for(int j=i+1;j<=n;j++)
                if(deg[j]>0){y=j;break;}
            break;
        }
    ch[x][ve[1]]=ch[ve[1]][x]=1;
    ch[y][ve[1]]=ch[ve[1]][y]=1;
    ch[x][ve[2]]=ch[ve[2]][x]=1;
    ch[y][ve[num]]=ch[ve[num]][y]=1;
    for(int i=2;i<num;i++){
        ch[ve[i]][ve[i+1]]=ch[ve[i+1]][ve[i]]=1;
    }
    print();return 0;
}

T3 乘积

以后补吧。

上一篇:Proxmox VE虚拟化管理平台-相关概念


下一篇:质因数分解模板