20190725-20190726

异或约数和

题意简述

求 $f_1\space xor\space f_2…\space xor\space f_n$ , $f_i$ 表示 $i$ 的所有约数的异或和。

$n\leq 10^{14}$ 。

$solution:$

考虑 $xor$ 有结合律与交换律,所以考虑优化给定式子。

对于 $1-n$ 有 $x$ 这种约数的有 $[\dfrac{n}{x}]$ ,直接查约数个数是否为奇数,时间复杂度 $O(n)$。

而对于 $[\dfrac{n}{x}]$ ,当 $x\leq \sqrt{n}$ 时 $[\dfrac{n}{x}]$ 个不相同。

而当 $x>\sqrt{n}$ 时 $[\dfrac{n}{x}]$ 只有 $\sqrt{n}$ 个取值范围,所以直接暴力维护一下 $[\dfrac{n}{x}]$ 即可。

时间复杂度 $O(\sqrt{n})$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
int n,ans;
inline int Qx(int ps){
    if(!ps) return 0;
    if(ps%4==0) return ps;
    if(ps%4==1) return 1;
    if(ps%4==2) return ps+1;
    if(ps%4==3) return 0;
}
inline int Xor(int l,int r){
    return Qx(r)^Qx(l-1);
}
int ps;
signed main(){
    freopen("Xor.in","r",stdin);
    freopen("Xor.out","w",stdout);
    n=read();
    ps=1;
    while(ps<=n){
        int l=ps,r=n/((int)(n/ps)),g=n/ps;
        if(g%2==1) ans^=Xor(l,r);
        ps=r+1;
    }printf("%lld\n",ans);return 0;
}
View Code

OR三元组

题意简述

$q$ 次询问,每次询问满足 $a_i|a_j|a_k=x,i<j<k$ 的三元组 $(i,j,k)$ 的个数。

$n,q\leq10^6,1\leq a_i,x\leq 255$ 。

$solution:$

可以现在 $O(255\times n)$ 时间内处理出 $f_{i,j}$ 表示前 $i$ 个数中有 $f_{i,j}$ 个数满足 $or\space x=x$ 。

考虑容斥,$Ans=\sum (1\space or\space -1)\dbinom{i}{3}\space(i\space or x\space =x)$ ,而正负 $1$ 直接判断二进制上含 $1$ 的个数差是奇还是偶。

正确性证明,考虑维恩图,$\dbinom{x}{3}$ 表示随机散点的个数,而最后要求的是散到最小的那块个数,因为是 $i\space or\space x=x$,所以最后散到的就是 $x$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=100001;
int dig[11],n,q,a[MAXN],f[MAXN][256];
int Q(int g){
    if(g<3) return 0;
    return (g*(g-1)*(g-2))/6;
}
int Qdig(int x){
    int Ans=0;
    while(x){
        Ans+=(x&1);
        x/=2;
    }return Ans;
    
}
signed main(){
    freopen("Or.in","r",stdin);
    freopen("Or.out","w",stdout);
    n=read(),q=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++){
        for(int j=0;j<=255;j++) if((a[i]&j)==a[i]) f[i][j]++;
    }
    for(int i=1;i<=n;i++)
        for(int j=0;j<=255;j++) f[i][j]+=f[i-1][j];
    for(int i=1;i<=q;i++){
        int l=read(),r=read(),x=read(),ans=0;
        for(int j=0;j<=x;j++){
            if((x|j)==x){
                int dig=Qdig(x)-Qdig(j);
                ans+=(dig%2?-1:1)*Q(f[r][j]-f[l-1][j]);
            }
        }printf("%lld\n",ans);
    }
}/*
3 1
1 1 1
1 3 1

*/
View Code

香农游戏

题意简述

$T$ 组询问,每次给定 $n$ 个点 $m$ 条边的图,问是否可以存在两组不相交的边集,可以是每个图均联通。

$T,n\leq 10,m\leq 100$ 。

$solution:$

考虑搜索,若存在图 $G_1,G_2$ 满足条件。

那么对于现在要搜索的边 $u,v$ ,若在 $G_1$ 中可以减少连通块,那么就选,否则不选。$G_2$ 同理,发现这样就过了。

因为边集搜索最高对于 $G$ 有 $10$ 条,所以最后的时间复杂度近似于 $O(2^{20}\times w)$ , $w$ 为一个比较小的常数。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=11;
int n;
struct Union{
    int f[MAXN],val,sta[MAXN];
    void init(){val=n;for(int i=1;i<=n;i++) f[i]=i;}
    int find(int x){
        if(f[x]==x) return x;
        return find(f[x]);
    }
    void merge(int x,int y){
        int t1=find(x),t2=find(y);
        f[t2]=t1;
        sta[++sta[0]]=t2;
        val--;
    }
    void back(){
        f[sta[sta[0]]]=sta[sta[0]];
        sta[0]--;
        val++;
    }
    void debug(){
        for(int i=1;i<=n;i++) printf("%d ",f[i]);
        printf("\n");return;
    }
}G1,G2;
int T,m;
struct node{
    int u,v;
}x[MAXN*MAXN];
bool flag;
void dfs(int ps){
    if(flag) return;
    if(G1.val==1&&G2.val==1){flag=1;return;}
    if(ps==m+1) return;
    bool F=1;
    if(G1.find(x[ps].u)!=G1.find(x[ps].v)){
        F=0;
        G1.merge(x[ps].u,x[ps].v);
        dfs(ps+1);
        G1.back();
    }
    if(G2.find(x[ps].u)!=G2.find(x[ps].v)){
        F=0;
        G2.merge(x[ps].u,x[ps].v);
        dfs(ps+1);
        G2.back();
    }
    if(F) dfs(ps+1);
    return;
}
void solve(){
    flag=0;
    n=read(),m=read();G1.init(),G2.init();
    for(int i=1;i<=m;i++) x[i].u=read(),x[i].v=read();
    dfs(1);
    if(flag) printf("Possible\n");
    else printf("Impossible\n");
    return;
}
int main(){
    freopen("Game.in","r",stdin);
    freopen("Game.out","w",stdout);
    T=read();
    while(T--) solve();
}
View Code

致富之路

题意简述

有 $m$ 个可供选择的左下角 , $n$ 个可供选择的右上角,问最大矩形面积。

$n,m\leq 10^5$ 。

$solution:$

考虑对于左下角的决策点,若 $x$ 相同时肯定 $y$ 最小的是最优的,而当 $x_i<x_j$ 时, $y_i>y_j$ 是更优的。

右上角决策点也如此,若 $x$ 相同时 $y$ 最大是最优的,而最后我们想看到的是 $x_i<x_j,y_i>y_j$ 的支持删除的,直接单调栈即可。

现在已经可以求出比较好的决策点,考虑右上角 $(x_1,y_1),(x_2,y_2),(x_1<x_2,y_1>y_2)$ 

设对于 $(x_1,y_1)$ 的最优左下决策点为 $(x,y)$ ,则对于 $(x_2,y_2)$ 的决策点 $(xx,yy)$ 一定 $x\leq xx$ ,可以画图或者推式子得到。

所以满足决策单调性,直接分治即可,时间复杂度 $O(n\log n)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<climits>
#define LL long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=500001;
int n,m,N,M;
struct Fac{
    int p,d;
}f[MAXN],F[MAXN];
bool cmp1(Fac x1,Fac x2){
    if(x1.p==x2.p) return x1.d<x2.d;
    return x1.p<x2.p;
}
struct Com{
    int q,e;
}g[MAXN],G[MAXN];
bool cmp2(Com x1,Com x2){
    if(x1.q==x2.q) return x1.e>x2.e;
    return x1.q<x2.q;
}
int Mx,My,k[MAXN];
LL Ans[MAXN];
stack<int> sta;
inline void solve(int l,int r,int L,int R){
    if(l>r) return;
    if(L<=0) L=1;
    int mid=l+r>>1,ps=-1;
    for(register int i=L;i<=R;++i){
        if(G[mid].q>=F[i].p&&G[mid].e>=F[i].d){
            LL calc=(LL)(G[mid].q-F[i].p)*(LL)(G[mid].e-F[i].d);
            if(calc>=Ans[mid]){Ans[mid]=calc,ps=i;}
        }
    }
    solve(l,mid-1,L,ps),solve(mid+1,r,ps,R);return;
}
signed main(){
    freopen("Rich.in","r",stdin);
    freopen("Rich.out","w",stdout);
    m=read(),n=read();
    for(int i=1;i<=m;i++) f[i].p=read(),f[i].d=read();
    for(int i=1;i<=n;i++) g[i].q=read(),g[i].e=read();
    sort(f+1,f+m+1,cmp1);
    int l=1;
    for(register int i=2;i<=m;++i){
        if(f[i].p==f[i-1].p) continue;
        F[++M]=f[l];
        l=i;
    }
    F[++M]=f[l];
    for(register int i=1;i<=M;i++) f[i]=F[i];
    m=M;
    M=0;F[++M]=f[1];
    for(register int i=2;i<=m;i++){
        if(F[M].d<=f[i].d) continue;
        F[++M]=f[i];
    }
    sort(g+1,g+n+1,cmp2);
    l=0;
    int Minn=INT_MAX;
    for(register int i=1;i<=n;++i){
        while(F[l+1].p<=g[i].q&&l<M) Minn=min(Minn,F[l+1].d),l++;
        if(Minn>g[i].e&&Minn!=LLONG_MAX) continue;
        G[++N]=g[i];
    }
    n=N;
    for(register int i=1;i<=N;++i) g[i]=G[i];
    l=1;N=0;
    for(register int i=2;i<=n;++i){
        if(g[i].q==g[i-1].q) continue;
        G[++N]=g[l];
        l=i;
    }
    G[++N]=g[l];
    n=N;
    for(register int i=1;i<=N;++i) g[i]=G[i];
    for(register int i=1;i<=n;++i){
        while(!sta.empty()){
            int xx=sta.top();
            if(g[xx].e<=g[i].e) sta.pop();
            else break;
        }
        sta.push(i);
    }
    while(!sta.empty()) k[++k[0]]=sta.top(),sta.pop();
    N=k[0];
    for(register int i=1;i<=N;i++) G[i]=g[k[N-i+1]];
    solve(1,N,1,M);
    LL Maxn=0ll;
    for(register int i=1;i<=N;i++) Maxn=max(Maxn,Ans[i]);
    printf("%lld\n",Maxn);return 0;
}
View Code

菌落

题意简述

共 $T$ 组询问。

有 $n$ 个菌落,每个菌落都有颜色与质量两种属性,最后通过操作将其化为 $1$ 个,问最小代价。

$n\leq 10,T\leq 10$ 。

$solution:$

因为 $n\leq 10$,考虑状压 $dp$ 或者搜索。

状压 $dp$ 可以将 $n$ 个数的并查集祖先记录下来,发现总状态数最多为 $10!$ ,直接转移即可。

时间复杂度 $O(能过)$ 。

搜索直接暴力贡献最小的几个,因为在绝大多数情况下,贪心与最优策略是一样的,所以就过了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<vector>
#include<climits>
#include<cmath>
#define int long long
#define LL long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=11;
struct node{
    int val,col;
}g[MAXN];
int T,n;
LL pw[MAXN],fac[MAXN];
int Get(int g[]){
    int ans=0;
    for(int i=2;i<=n;i++) ans+=(g[i]-1)*fac[i-1];
    return ans;
}
int* Get1(int *EE,int Ans){
    EE[0]=0;
    int Fac=fac[n-1];
    for(int i=n-1;i>=1;--i){
        EE[i+1]=(Ans/Fac)+1;
        Ans%=Fac;
        Fac/=i;
    }
    EE[1]=1;return EE;
}
int s[MAXN],tot;
LL f[36287800];
vector<int> ve,V;
char str[MAXN];
int E[MAXN],S[MAXN],ans0[MAXN],ans1[MAXN];
inline LL W(int s[]){
    LL sum=0;
    for(register int i=1;i<=n;++i){
        if(!g[i].col) ans0[s[i]]+=g[i].val;
        else ans1[s[i]]+=g[i].val;
    }
    for(register int i=1;i<=n;++i){
        if(s[i]==i) {
            sum+=(LL)abs(ans1[i]-ans0[i])*(LL)abs(ans1[i]-ans0[i]);
            ans1[i]=ans0[i]=0;
        }
    }
    return sum;
}
LL Minn,P,Num,GG,H;
int cnt;
bool MAP[36287800];
int bug[MAXN];
inline void solve(){
    n=read();Minn=LLONG_MAX;
    for(register int i=1;i<=n;++i){
        int val=read();scanf("%s",str+1);
        g[i].val=val;
        if(str[1]=='7') g[i].col=0;
        else g[i].col=1;
    }
    tot=0;
    memset(f,127/3,sizeof(f));
    for(register int i=1;i<=n;++i) s[i]=i;
    Num=Get(s);
    f[Num]=0;f[Num]=0;
    ve.push_back(Num);
    for(register int i=1;i<=n-1;++i){
        int siz=ve.size();
        for(register int j=0;j<siz;++j){
            P=ve[j];
            Get1(s,P);
            E[0]=0;
            for(register int k=1;k<=n;++k)
                if(s[k]==k) E[++E[0]]=k;
            for(register int k1=1;k1<=E[0];++k1){
                for(register int k2=k1+1;k2<=E[0];++k2){
                    for(register int k=1;k<=n;++k){
                        cnt++;
                        if(s[k]==E[k2]) S[k]=E[k1];
                        else S[k]=s[k];
                    }    
                    GG=Get(S);
                    if(MAP[GG]==0){MAP[GG]=1;V.push_back(GG);}
                    H=GG;
                    f[H]=min(f[H],f[P]+W(S));
                    if(i==n-1) Minn=min(Minn,f[H]);
                }
            }
        }
        ve.clear();
        siz=V.size();
        for(register int j=0;j<siz;++j) ve.push_back(V[j]);
        V.clear();
    }
    memset(MAP,0,sizeof(MAP));
    printf("%lld\n",Minn);return;
}
signed main(){
    freopen("germ.in","r",stdin);
    freopen("germ.out","w",stdout);
    T=read();fac[0]=1ll;
    for(int i=1;i<=10;i++) fac[i]=fac[i-1]*i;
    while(T--) solve();
    return 0;
}
View Code

石头

题意简述

$T$ 组询问。

每次求环形最长非严格上升子序列长度,保证数据随机。

$n\leq 10^4,T\leq 10$

$solution:$

对于数据随机必定答案不大,考虑 $dp$ 。

设 $f_{i,j}$ 表示以 $i$ 为结尾,$LIS$ 长度为 $j$ 时的最右左端点。

$f_{i,j}=max{f_{k,j-1}}\space(k<i,a_k\leq a_i)$ ,直接数据结构优化即可。

因为 $j$ 不会很大所以时间复杂度为 $O(答案\times n\log n)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=20001;
int n,a[MAXN],T,f[MAXN][2];
struct Bit{
    int maxn[MAXN];
    void clear(){memset(maxn,-127/3,sizeof(maxn));return;}
    int lowbit(int x){return x&-x;}
    int Query(int x){
        int Maxn=INT_MIN;
        for(int i=x;i;i-=lowbit(i)) Maxn=max(Maxn,maxn[i]);
        return Maxn;
    }
    void modify(int x,int w){
        for(int i=x;i<=n;i+=lowbit(i)) maxn[i]=max(maxn[i],w);
        return;
    }
}bit;
void solve(){
    n=read();
    for(register int i=1;i<=n;++i) a[i+n]=a[i]=read();
    for(register int i=1;i<=2*n;++i) f[i][0]=i;
    int cur=0;
    for(register int k=2;;++k){
        bit.clear();
        bool flag=0;cur^=1;
        for(int i=1;i<=2*n;i++){
            f[i][cur]=bit.Query(a[i]),bit.modify(a[i],f[i][cur^1]);
            if(i-f[i][cur]+1<=n) flag=1;
        }if(!flag){printf("%d\n",k-1);return;}
    }
}
int main(){
    freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);
    T=read();
    while(T--) solve();
}
View Code

翻转项链

题意简述

一个长度为 $n$ 的序列,你可以翻转一段区间后求环状最大连续子段和,不能非空,求最大值。

$n\leq 10^5$ 。

$solution:$

考虑若翻转区间和最大连续子段和无交集则翻转无用,所以必定有交。

若两端全有交可以等价于无交,所以只有一段有交时有用的。

所以问题变为了求环状最大两段子段和,直接分类讨论 $dp$ 即可。

时间复杂度 $O(n)$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=200011;
int n,Maxn,a[MAXN],Maxn1[MAXN],Maxn2[MAXN],sum,Minn1[MAXN],Minn2[MAXN];
int cnt;
signed main(){
    freopen("reverse.in","r",stdin);
    freopen("reverse.out","w",stdout);
//    freopen("10.in","r",stdin);
    n=read();
    Maxn=-LLONG_MAX;
    for(int i=1;i<=n;i++){
        a[i]=read(),sum+=a[i];
        if(a[i]>=0) cnt++;
    }
    if(!cnt){
        sort(a+1,a+n+1);
        printf("%lld\n",a[n]);
        return 0;
    }
    if(cnt==1){
        sort(a+1,a+n+1);
        printf("%lld\n",a[n]);
        return 0;
    }
    memset(Maxn1,-127/3,sizeof(Maxn1)),memset(Maxn2,-127/3,sizeof(Maxn2));
    memset(Minn1,127/3,sizeof(Minn1)),memset(Minn2,127/3,sizeof(Minn2));
    for(int i=1;i<=n;i++) Maxn1[i]=max(Maxn1[i-1]+a[i],a[i]),Minn1[i]=min(Minn1[i-1]+a[i],a[i]);
    for(int i=1;i<=n;i++) Maxn1[i]=max(Maxn1[i-1],Maxn1[i]),Minn1[i]=min(Minn1[i-1],Minn1[i]);
    for(int i=n;i>=1;i--) Maxn2[i]=max(Maxn2[i+1]+a[i],a[i]),Minn2[i]=min(Minn2[i+1]+a[i],a[i]);
    for(int i=n;i>=1;i--) Maxn2[i]=max(Maxn2[i+1],Maxn2[i]),Minn2[i]=min(Minn2[i+1],Minn2[i]);
    for(int i=1;i<n;i++){
        Maxn=max(Maxn,Maxn1[i]+Maxn2[i+1]),Maxn=max(Maxn,sum-Minn1[i]-Minn2[i+1]);
    }
    printf("%lld\n",Maxn);return 0;
}/*
3
-1 1 -1
*/
View Code

 

posted @ 2019-07-27 13:31  siruiyang_sry  阅读(172)  评论(0编辑  收藏  举报