zoukankan      html  css  js  c++  java
  • 2021“MINIEYE杯”中国大学生算法设计超级联赛2

    2021“MINIEYE杯”中国大学生算法设计超级联赛2

    1001 I love cube

    问一个边长为((n-1))的立方体中的有多少等边三角形。
    容易发现等边三角形的三个顶点一定要在边上且距离一个立方体的顶点等距离。
    本来以为将边长为((n-2))的立方体的答案求出可以递推(上一次答案填上新立方体的8个角落,之后加上新的等边三角形)。
    但是这样显然会算重。
    后来发现枚举等边三角形距离立方体的顶点的距离可以算出答案。

    (当时为什么一直没有怀疑自己算法的正确性?)

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int p=1e9+7;
    int read(){
        int sum=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
        return sum*f;
    }
    int ksm(int x,int b){
        int ans=1;
        while(b){
            if(b&1)ans=ans*x%p;
            b>>=1;
            x=x*x%p;
        }
        return ans;
    }
    signed main(){
        int T=read();
        while(T--){
            int n=read() - 1;
            if(n == -1) printf("0
    ");
            else
            {
                int x1 = (((2 * (n % p)) % p) * (n % p)) %p;
                int x2 = (((n + 1) %p) * ((n + 1) %p)) % p;
                printf("%lld
    ",x1 * x2 %p);
            }
        }
        return 0;
    }
    

    1002 I love tree

    在树上的一条链上加一个等差数列的平方,单点查询
    考虑如何用树剖来维护。
    感觉思路很厉害。
    问题可以转化为给区间加二次函数(树剖就是把一个链分成log个区间)
    不妨设对(dfn)([a,b])这一段区间增加一个从1开始的等差数列的平方
    可以看做区间加(dfn-(a-1)),然后展开得到,(dfn^2-2*(a-1)*dfn+(a-1)^2)
    可以转化成维护加了多少个(dfn^2)(dfn),常数。
    然后这样的话其实就是区间加定值((1,-2*(a-1),(a-1)^2))
    这就是熟悉的树剖问题了。
    (PS:树剖跳链和这个过程中的线段树区间加十分难写,但是好在是单点查询,这就是出题人的温柔吗?但是当我看到之后线段树维护矩阵之后就不会这样想了)

    具体看代码:

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    #define N 101000
    #define mid (L+R>>1)
    #define ls now<<1
    #define rs now<<1|1 
    #define int long long
    int top[N],fa[N],dep[N],size[N],mx_son[N],dfn[N],tot,n;
    int sum_a[N*40],sum_b[N*40],sum_c[N*40]; 
    struct edge{
        int to,nxt;
    }e[N*2];
    int cnt,head[N];
    void add_edge(int u,int v){
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    int read(){
        int sum=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
        return sum*f;
    }
    int get_lca(int x,int y){
        int fx=top[x],fy=top[y];
        while(fx!=fy){
            if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
            x=fa[fx],fx=top[x];
        }
        if(dep[x]<dep[y])return x;
        else return y;
    }
    void dfs(int u,int f,int deep){
        dep[u]=deep;fa[u]=f;size[u]=1;mx_son[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(v==f)continue;
            dfs(v,u,deep+1);
            size[u]+=size[v];
            if(size[mx_son[u]]<size[v])mx_son[u]=v;
        }
    }
    void get_top(int u,int tp){
        dfn[u]=++tot;
        top[u]=tp;
        if(mx_son[u])get_top(mx_son[u],tp);
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa[u]||v==mx_son[u])continue;
            get_top(v,v);
        }
    }
    void add(int L,int R,int l,int r,int w,int type,int now){
        if(L==l&&R==r){
            sum_a[now]+=type;
            sum_b[now]+=type*(-2*w);
            sum_c[now]+=type*w*w;
            return;
        }
        if(l>mid)add(mid+1,R,l,r,w,type,rs);
        else if(r<=mid)add(L,mid,l,r,w,type,ls);
        else add(L,mid,l,mid,w,type,ls),add(mid+1,R,mid+1,r,w,type,rs);
    }
    int query(int L,int R,int x,int now){
        int tmp=x*x*sum_a[now]+x*sum_b[now]+sum_c[now];
        if(L==R)return tmp;
        if(x>mid)return query(mid+1,R,x,rs)+tmp;
        else return query(L,mid,x,ls)+tmp;
    }
    void add(int x,int y){
        int fx=top[x],fy=top[y];
        int lca=get_lca(x,y);
        int L;
        L=-2*dep[lca]+dep[y]+dep[x]+1;
        int cnt=1;
        while(dep[fx]>dep[lca]){
            add(1,n,dfn[fx],dfn[x],dfn[x]+cnt,1,1);
            cnt+=dep[x]-dep[fx]+1;
            x=fa[fx],fx=top[x];
        } 
        add(1,n,dfn[lca],dfn[x],dfn[x]+cnt,1,1);
        cnt=0;
        while(dep[fy]>dep[lca]){
            cnt+=dep[y]-dep[fy]+1;
            add(1,n,dfn[fy],dfn[y],dfn[fy]-(L-cnt+1),1,1);
            y=fa[fy],fy=top[y];
        }
        cnt+=dep[y]-dep[lca]+1;
        add(1,n,dfn[lca],dfn[y],dfn[lca]-(L-cnt+1),1,1);
        add(1,n,dfn[lca],dfn[lca],dfn[lca]-(L-cnt+1),-1,1);
    }
    signed main(){
        n=read();
        for(int i=1;i<n;i++){
            int u=read(),v=read();
            add_edge(u,v);
            add_edge(v,u);
        }
        dfs(1,0,1);
        get_top(1,1);
        int m=read();
        while(m--){
            int type=read();
            if(type==1){
                int x=read(),y=read();
                add(x,y);
            }
            if(type==2){
                int x=read();
                printf("%lld
    ",query(1,n,dfn[x],1)); 
            }
        }
        return 0;
    }
    

    1003 I love playing games

    我们可以先对(z)点做一遍最短路
    如果(dis[x]<dis[y])先手必胜
    如果(dis[y]<dis[x])后手必胜
    这是比较显然的。
    如果(dis[x]=dis[y])我们利用(dp)来判断一下
    (dp[i][j][0/1])代表当前x在i点, y在j点,当前轮到谁走,dp值代表必胜/平局/必输
    然后在最短路图上转移即可。
    由于最短路边的性质,每次只会让x和y更接近z。
    时间复杂度为(O(n^2))

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define N 1010
    #define M 201000
    #define INF 1e9
    bool E[N][N];
    int n,m,x,y,z;
    int read(){
        int sum=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
        return sum*f;
    }
    struct edge{
        int to,nxt,w;
    }e[M];
    int cnt,head[N];
    void add_edge(int u,int v,int w){
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        e[cnt].w=1;
        head[u]=cnt;
    }
    struct node{
        int id,dis;
        node (int x,int y){
            id=x,dis=y;
        }
    };
    int dis[N];
        queue<int> q;
    void bfs(){
        for(int i=1;i<=n;i++)dis[i]=INF;
        dis[z]=0;
        q.push(z);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=head[u];i;i=e[i].nxt){
                int v=e[i].to;
                if(dis[v]==INF){
                    dis[v]=dis[u]+1;
                    q.push(v);
                }
            }
        }
    }
    int dp[N][N][2];
    int get_dp(int x,int y,int turn){
        if(x==z&&y==z&&turn==0)return 2;
        if(x==z&&y!=z&&turn==0)return 1;
        if(x!=z&&y==z)return 3;
        if(dp[x][y][turn])return dp[x][y][turn];
        if(turn==0){
            bool flag=0;
            for(int i=1;i<=n;i++){
                if(E[x][i]==0)continue;
                if(i==y)continue;
                if(get_dp(i,y,1)==1){
                    dp[x][y][turn]=1;
                    return 1;
                }
                if(dp[i][y][1]==2)flag=1;
            }
            if(flag)dp[x][y][turn]=2;
            else dp[x][y][turn]=3;
            return dp[x][y][turn];
        }
        if(turn==1){
            bool flag=0;
            for(int i=1;i<=n;i++){
                if(E[y][i]==0)continue;
                if(i==x){
                    if(i==z)flag=1;
                    continue;
                }
                if(get_dp(x,i,0)==3){
                    dp[x][y][turn]=3;
                    return 3;
                }
                if(dp[x][i][0]==2)flag=1;
            }
            if(flag)dp[x][y][turn]=2;
            else dp[x][y][turn]=1;
            return dp[x][y][turn];
        }
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m); 
            scanf("%d%d%d",&x,&y,&z);  
            cnt=0;
            for(int i=1;i<=n;i++)head[i]=0;
            int u,v;
            for(int i=1;i<=m;i++){
                scanf("%d%d",&u,&v);
                add_edge(u,v,1);
                add_edge(v,u,1);
            }
            bfs();
            if(dis[x]<dis[y]){printf("1
    ");continue;}
            if(dis[x]>dis[y]){printf("3
    ");continue;}
            if(dis[x]==INF){printf("2
    ");continue;}
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    E[i][j]=dp[i][j][0]=dp[i][j][1]=0;
            for(int u=1;u<=n;u++)
                for(int i=head[u];i;i=e[i].nxt){
                    int v=e[i].to;
                    if(dis[v]==dis[u]+e[i].w)E[v][u]=1;
                }
            printf("%d
    ",get_dp(x,y,0));
        }
        return 0;
    } 
    

    1004 I love counting

    跟上一场的某个题差不多。
    问一个区间的不同()的个数,这种问题可以使用HH的项链的思路。
    套一个01树上的判断。
    直接写树状数组套01trie就好了。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define N 101000
    int sum[N*400],ch[N*400][2],tot,n,m,root[N];
    int a[N],pre[N],pos[N],ans[N];
    struct qu{
        int l,r,a,b,num;
    }q[N];
    int read(){
        int sum=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
        return sum*f;
    }
    void update(int now){
        sum[now]=sum[ch[now][0]]+sum[ch[now][1]];
    }
    void add(int tall,int x,int w,int &now){
        if(now==0)now=++tot;
        if(tall<0){
            sum[now]+=w;
            return;
        }
        int bit=x>>tall&1;
        add(tall-1,x,w,ch[now][bit]);
        update(now);
    }
    int check(int a,int b,int now){
        int ans=0;
        for(int i=20;i>=0;i--){
            int v = b >> i & 1;
               int w = a >> i & 1;
            if(v == 0){
                if(sum[ch[now][w]]) now = ch[now][w];
                else return ans;
            }
            else{
                if(sum[ch[now][w]]){
                    ans += sum[ch[now][w]];
                }
                if(sum[ch[now][w ^ 1]]) now = ch[now][w ^ 1];
                else return ans;
            }
        }
        return ans+sum[now];
    }
    bool cmp(qu a,qu b){
        return a.r<b.r;
    }
    int lowbit(int x){
        return x&-x;
    }
    void ADD(int x,int val,int w){
        for(int i=x;i<=n;i+=lowbit(i)){
            add(20,val,w,root[i]);
        }
    }
    int query(int x,int l,int r){
        int ans=0;
        for(int i=x;i>=1;i-=lowbit(i)){
            ans+=check(l,r,root[i]);
        }
        return ans;
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++){
            a[i]=read();
            pre[i]=pos[a[i]]+1;
            pos[a[i]]=i;
        }
        m=read();
        for(int j=1;j<=m;j++)q[j].l=read(),q[j].r=read(),q[j].a=read(),q[j].b=read(),q[j].num=j;
        sort(q+1,q+1+m,cmp);
        for(int now=0,i=1;i<=m;i++){
            while(now<q[i].r){
                now++;
                ADD(pre[now],a[now],+1);
                ADD(now+1,a[now],-1);
            }
            ans[q[i].num]=query(q[i].l,q[i].a,q[i].b);
        }
        for(int i=1;i<=m;i++)
            printf("%d
    ",ans[i]);
        return 0; 
    }
    

    1005 I love string

    签到题,发现答案跟第一个字母连续出现的次数有关。所以设第一个字母连续出现了x次答案就是(2^x)

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define p 1000000007
    int ans[101000];
    char a[101000];
    void pre_work(){
        ans[1]=1;
        for(int i=2;i<=100000;i++)ans[i]=ans[i-1]*2%p;
    }
    int read(){
        int sum=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
        return sum*f;
    }
    int main(){
        int T=read();
        pre_work();
        while(T--){
            int n=read();
            scanf("%s",a+1); 
            for(int i=2;i<=n;i++){
                if(a[i]!=a[i-1]){
                    cout<<ans[i-1]<<endl;
                    break;
                }
            }
        }
        return 0;
    }
    

    1007 I love data structure

    难点是操作2。
    考虑用矩阵去维护。
    操作2相当于对(left (egin {array} {l}x & y \0 & 0end {array} ight )) 乘上(left (egin {array} {l}3 & 3 \2 & -2end {array} ight )) 操作3则是乘上(left (egin {array} {l}0 & 1 \1 & 0end {array} ight ))
    考虑答案怎么维护,加法标记很好维护。
    设乘的矩阵是(left (egin {array} {l}a & b \c & dend {array} ight ))
    得到到xy矩阵是 (left (egin {array} {l}a*x+c*y & b*x+d*y \ 0 & 0end {array} ight ))
    (x'=ax+cy y'=bx+dy),所以 (x'y'=abx^2+cdy^2+(ad+bc)xy),于是如果要维护xy就还要维护(x^2)(y^2)
    (x^2)为例维护(x'^2=a^2x^2+c^2y^2+2acxy)发现不用维护更多的东西了。
    所以只要维护(sum x) (sum y) (sum x^2) (sum y^2) (sum xy)
    矩阵的标记显然是矩阵乘法维护。那么维护加法标记呢?
    考虑(left (egin {array} {l}x+n & y+m \0 & 0end {array} ight ))乘上(left (egin {array} {l}a & b \c & dend {array} ight ))得到(left (egin {array} {l}ax+cy+an+cm & bx+dy+bn+dm \0 & 0end {array} ight ))(left (egin {array} {l}x'+an+cm & y'+bn+dm \0 & 0end {array} ight ))
    发现加法标记变成 (n'=an+cm m'=bn+dm)
    记住要先下推矩阵乘法的标记

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int p=1e9+7;
    #define mid (L+R>>1)
    #define ls now<<1
    #define rs now<<1|1
    #define N 201000
    int sum[N*4][2],sum_p[N*4][2],sum_m[N*4],lazy[N*4][2];
    int a[N],b[N];
    int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    } 
    struct matrix{
    	int a[2][2]={{1,0},{0,1}};
    }e,change,lazy_m[N*4];
    matrix operator *(matrix a,matrix b){
    	matrix c;
    	for(int i=0;i<=1;i++)
    		for(int j=0;j<=1;j++){
    			c.a[i][j]=0;
    			for(int k=0;k<=1;k++)
    				c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%p;
    		}
    	return c;
    }
    void work(int L,int R,int l,int r,matrix w,int now);
    void pushdown(int L,int R,int now);
    void add(int L,int R,int l,int r,int w,int type,int now);
    void update(int now){
    	sum[now][0]=(sum[ls][0]+sum[rs][0])%p;
    	sum[now][1]=(sum[ls][1]+sum[rs][1])%p;
    	sum_p[now][0]=(sum_p[ls][0]+sum_p[rs][0])%p;
    	sum_p[now][1]=(sum_p[ls][1]+sum_p[rs][1])%p;
    	sum_m[now]=(sum_m[ls]+sum_m[rs])%p;
    }
    void pushdown(int L,int R,int now){
    	work(L,mid,L,mid,lazy_m[now],ls);
    	work(mid+1,R,mid+1,R,lazy_m[now],rs); 
    	lazy_m[now].a[0][0]=1;
    	lazy_m[now].a[0][1]=0;
    	lazy_m[now].a[1][0]=0;
    	lazy_m[now].a[1][1]=1;
    	for(int type=0;type<=1;type++){
    		add(L,mid,L,mid,lazy[now][type],type,ls);
    		add(mid+1,R,mid+1,R,lazy[now][type],type,rs);
    		lazy[now][type]=0;
    	}
    }
    void add(int L,int R,int l,int r,int w,int type,int now){
    	if(L==l&&R==r){
    		lazy[now][type]=(lazy[now][type]+w)%p;
    		sum_p[now][type]=(sum_p[now][type]+2ll*sum[now][type]%p*w%p+w*w%p*(R-L+1)%p)%p;
    		sum[now][type]=(sum[now][type]+w*(R-L+1)%p)%p;
    		sum_m[now]=(sum_m[now]+sum[now][type^1]*w%p)%p;
    		
    		return ;
    	}
    	pushdown(L,R,now);
    	if(l>mid)add(mid+1,R,l,r,w,type,rs);
    	else if(r<=mid)add(L,mid,l,r,w,type,ls);
    	else add(L,mid,l,mid,w,type,ls),add(mid+1,R,mid+1,r,w,type,rs);
    	update(now); 
    }
    void work(int L,int R,int l,int r,matrix w,int now){
    	if(L==l&&R==r){
    		int x=sum[now][0],y=sum[now][1],x_p=sum_p[now][0],y_p=sum_p[now][1],ans=sum_m[now];
    		int a=w.a[0][0],b=w.a[0][1],c=w.a[1][0],d=w.a[1][1];
    		sum_m[now]=(x_p*a%p*b%p+y_p*c%p*d%p+ans*(a*d%p+b*c%p)%p)%p;
    		sum_p[now][0]=(a*a%p*x_p%p+c*c%p*y_p%p+2ll*a*c%p*ans%p)%p;
    		sum_p[now][1]=(b*b%p*x_p%p+d*d%p*y_p%p+2ll*b*d%p*ans%p)%p;
    		sum[now][0]=(a*x%p+c*y%p)%p;
    		sum[now][1]=(b*x%p+d*y%p)%p;
    		x=lazy[now][0],y=lazy[now][1];
    		lazy[now][0]=(x*a%p+y*c%p)%p;
    		lazy[now][1]=(x*b%p+y*d%p)%p;
    		lazy_m[now]=lazy_m[now]*w;
    		return;
    	}
    	pushdown(L,R,now);
    	if(l>mid)work(mid+1,R,l,r,w,rs);
    	else if(r<=mid)work(L,mid,l,r,w,ls);
    	else work(L,mid,l,mid,w,ls),work(mid+1,R,mid+1,r,w,rs);
    	update(now);
    }
    int query(int L,int R,int l,int r,int now){
    	if(L==l&&R==r)return sum_m[now];
    	pushdown(L,R,now);
    	int ans=0;
    	if(l>mid)return query(mid+1,R,l,r,rs);
    	else if(r<=mid)return query(L,mid,l,r,ls);
    	else return (query(L,mid,l,mid,ls)+query(mid+1,R,mid+1,r,rs))%p; 
    	return ans%p;
    }
    void build(int L,int R,int now){
    	if(L==R){
    		sum[now][0]=a[L];
    		sum[now][1]=b[L];
    		sum_p[now][0]=a[L]*a[L]%p;
    		sum_p[now][1]=b[L]*b[L]%p;
    		sum_m[now]=a[L]*b[L]%p;
    		return;
    	}
    	build(L,mid,ls);
    	build(mid+1,R,rs);
    	update(now);
    }
    signed main(){
    	int n=read();
    	e.a[0][0]=3,e.a[0][1]=3;
    	e.a[1][0]=2,e.a[1][1]=p-2;
    	change.a[0][0]=0,change.a[0][1]=1;
    	change.a[1][0]=1,change.a[1][1]=0;
    	for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
    	build(1,n,1);
    	int m=read();
    	while(m--){
    		int type=read();
    		if(type==1){
    			int tag=read(),l=read(),r=read(),x=read();
    			add(1,n,l,r,x,tag,1);
    		}
    		if(type==2){
    			int l=read(),r=read();
    			work(1,n,l,r,e,1);
    		}
    		if(type==3){
    			int l=read(),r=read();
    			work(1,n,l,r,change,1);
    		}
    		if(type==4){
    			int l=read(),r=read();
    			printf("%lld
    ",query(1,n,l,r,1));
    		}
    	}
    	return 0;
    } 
    

    1008 Minimum spanning tree

    两遍背包,第一次求出(mn[i][j])表示i科目得到j分的最小天数。

    第二遍把求出来的(mn[i][j])当做物品再求一次背包。

    (01背包一个物品只能那一次所以要从后往前DP)

    我的dp写挂了,队友重写了一遍之后就过了。

    #include <bits/stdc++.h>
    #define ll long long
    #define int ll 
    #define mod 100000000
    #define N 100010
    #define long_long_MAX 9187201950435737471
    #define long_long_MIN -9187201950435737472
    #define int_MAX 2139062143
    #define int_MIN -2139062144
    #define jh(x, y) (x ^= y ^= x ^= y)
    #define loc(x, y) ((x - 1) * m + y)
    #define lowbit(x) (x & -x)
    using namespace std;
    
    ll max(ll x,ll y){return x > y ? x : y;}
    
    ll min(ll x,ll y){return x > y ? y : x;}
    
    inline ll read()
    {
        ll a=0;ll f=0;char p=getchar();
        while(!isdigit(p)){f|=p=='-';p=getchar();}
        while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();}
        return f?-a:a;
    }
    
    inline void print(ll x)
    {
        if(!x) return;
        if(x) print(x/10);
        putchar(x%10+'0');
    }
    
    struct node
    {
        int x,y;
    }w[55][15010];
    
    string na[100];
    map<string,int> mp;
    
    int mn[55][110];
    int dp[55][550][5];
    int T,t,n,m,p,cnt[55];
    
    signed main()
    {
        T = read();
        while(T--)
        {
            memset(mn,1,sizeof(mn));
            memset(dp,-1,sizeof(dp));
            memset(cnt,0,sizeof(cnt));
            mp.clear();
            cin>>n;
            for(int i = 1;i <= n;++i) cin >> na[i],mp[na[i]] = i,cnt[i] = 0;
            cin>>m;
            for(int i = 1;i <= m;++i)
            {
                string x;
                cin >> x;
                int d=mp[x]; 
                cin >> w[d][++cnt[d]].x;
                cin >> w[d][cnt[d]].y;
            }
            t = read(),p = read();
            //mn[i][j]表示i科目得到j分的最小天数
            for(int i=1;i<=n;i++){
                mn[i][0]=0;
                for(int k=1;k<=cnt[mp[na[i]]];k++)
                    for(int j=100;j>=0;j--){
                        if(j-w[mp[na[i]]][k].x>=0)
                        mn[i][j]=min(mn[i][j],mn[i][j-w[mp[na[i]]][k].x]+w[mp[na[i]]][k].y);
                    }
            }
            dp[0][0][0]=0;//dp[i][j][k]表示前i科目用了j天,k个不及格的最大价值
            for(int i = 1;i <= n;++i)
            {
                for(int j = t;j >= 0;--j)
                {
                    for(int k = p;k >= 0;--k)
                    {
                        for(int l = 59;l >= 0;--l)
                        {
                            if(k == 0 || j - mn[i][l] < 0) continue;
                            if(dp[i - 1][j - mn[i][l]][k - 1] == -1) continue;
                            dp[i][j][k] = max(dp[i][j][k],dp[i - 1][j - mn[i][l]][k - 1] + l);
                        }
                        for(int l = 100;l >= 60;--l)
                        {
                            if(j - mn[i][l] < 0) continue;
                            if(dp[i - 1][j - mn[i][l]][k] == -1) continue;
                            dp[i][j][k] = max(dp[i][j][k],dp[i - 1][j - mn[i][l]][k] + l);
                        }
                    }
                }
            }
    
            /*for(int i=1;i<=n;i++){
                for(int j=t;j>=0;j--)
                    for(int k=p;k>=0;k--)
                        dp[i][j][k]=dp[i-1][j][k];
                for(int j=t;j>=0;j--)
                    for(int k=p;k>=0;k--){
                        for(int l=0;l<=59;l++){
                            if(j-mn[i][l]<0)continue;
                            if(k==0)continue;
                            dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-mn[i][l]][k-1]);
                        }
                        for(int l=60;l<=100;l++){
                            if(j-mn[i][l]<0)continue;
                            dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-mn[i][l]][k]);
                        }
                    }
            }*/
            int ans = -1;
            for(int i = 0;i <= t;++i)
                for(int j = 0;j <= p;++j)
                    ans = max(ans,dp[n][i][j]);
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    1010 I love permutation

    这个推式子有点强。

    要求一个([0...p-1])的排列逆序对数的奇偶。

    可以列出这样的式子:
    (prod_{0<=i<j<=p-1}frac{f[j]-f[i]}{j-i})
    它的正负就是逆序对数的奇偶。
    考虑如何化简
    (prod_{0<=i<j<=p-1}frac{f[j]-f[i]}{j-i}=prod_{0<=i<j<=p-1}frac{a*j mod p-a*i mod p}{j-i}=prod_{0<=i<j<=p-1}a mod p)
    这里都是在(mod p)意义下的,所以最后一步乍一看是把正负数都变成正数有问题,但其实并没有。
    就好像你在处理一个在mod 998244353 的计数题时把负数x变成x+p。
    (原式=a^{frac{p(p-1)}{2}} mod p=a^{p^{frac{(p-1)}{2}}}mod p)
    因为欧拉定理(a^p=a mod p)
    所以(原式=a^{frac{p-1}{2}})
    判断其是1或者p-1然后就能知道逆序对数的奇偶了。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    int p;
    int read(){
        int sum=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
        return sum*f;
    }
    __int128 ksm(int x,int b){
        __int128 ans=1;
        while(b){
            if(b&1)ans=ans*x%p;
            b>>=1;
            x=(__int128)x*x%p;
        } 
        return ans;
    }
    signed main(){
        int T=read();
        while(T--){
            int a=read();
            p=read();
            int ans=ksm(a%p,(p-1)/2);
            if(ans==1)cout<<"0"<<endl;
            else cout<<"1"<<endl;
        } 
        return 0;
    } 
    

    1011 I love max and multiply

    经典的子集DP都是枚举所有子集,但是本题求的
    (C_k=max(a_ib_j) (i&j>=k))显然可以看成(C_k=max(C_x,a_ib_j) (i&j=k,x>k))
    所以本题重点是要求(C_k=max(a_ib_j) (i&j=k))
    然后再放宽限制求(C_k=max(a_ib_j) (kin i&j))这不会让答案错误。
    对于一个k,分开维护(a_i),(b_j)的最大最小值。
    然后考虑在经典子集dp的基础上优化状态的转移,发现一个集合会从所有包含它的集合转移,这太浪费时间了。其实只考虑从比它大1的集合转移就行了。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    #define p 998244353
    #define N 310000
    #define INF 1e18 
    #define int long long
    int mx[2][N],mn[2][N];
    long long c[N];
    int read(){
        int sum=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
        return sum*f;
    }
    int a[N],b[N];
    signed main(){
        int T=read();
        while(T--){
            int n=read();
            for(int i=0;i<n;i++){
                a[i]=read();
                mx[0][i]=mn[0][i]=a[i];
            }
            for(int i=0;i<n;i++){
                b[i]=read();
                mx[1][i]=mn[1][i]=b[i];
            }
            for(int i=n-1;i>=0;i--){
                for(int j=1;j<=i;j<<=1){
                    if((j&i)==0)continue;
                     mn[0][i^j]=min(mn[0][i^j],mn[0][i]);
                     mn[1][i^j]=min(mn[1][i^j],mn[1][i]);
                     mx[0][i^j]=max(mx[0][i^j],mx[0][i]);
                     mx[1][i^j]=max(mx[1][i^j],mx[1][i]);
                }
            }
            int ans=0;
            c[n]=-INF;
            for(int i=n-1;i>=0;i--){
                c[i]=c[i+1];
                c[i]=max(mx[0][i]*mx[1][i],c[i]);
                c[i]=max(mn[0][i]*mn[1][i],c[i]);
                c[i]=max(mn[0][i]*mx[1][i],c[i]);
                c[i]=max(mn[1][i]*mx[0][i],c[i]);
                ans=(ans+c[i]+p)%p;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    1012 I love 114514

    签到题,有没有114514在给出的字符串中出现过。枚举起点匹配判断即可。

    #include <bits/stdc++.h>
    #define ll long long
    #define int ll 
    #define mod 100000000
    #define N 100010
    #define long_long_MAX 9187201950435737471
    #define long_long_MIN -9187201950435737472
    #define int_MAX 2139062143
    #define int_MIN -2139062144
    #define jh(x, y) (x ^= y ^= x ^= y)
    #define loc(x, y) ((x - 1) * m + y)
    #define lowbit(x) (x & -x)
    using namespace std;
    
    ll max(ll x,ll y){return x > y ? x : y;}
    
    ll min(ll x,ll y){return x > y ? y : x;}
    
    inline ll read()
    {
        ll a=0;ll f=0;char p=getchar();
        while(!isdigit(p)){f|=p=='-';p=getchar();}
        while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();}
        return f?-a:a;
    }
    
    inline void print(ll x)
    {
        if(!x) return;
        if(x) print(x/10);
        putchar(x%10+'0');
    }
    
    int n,T;
    char a[N];
    
    signed main()
    {
        T = read();
        while(T--)
        {
            scanf("%s",a + 1);
            n = strlen(a + 1);
            int ok = 0;
            for(int i = 1;i <= n - 5;++i)
            {
                if(a[i] == '1' && a[i + 1] == '1' && a[i + 2] == '4' && a[i + 3] == '5' && a[i + 4] == '1' && a[i + 5] == '4')
                {
                    ok = 1;
                    printf("AAAAAA
    ");
                    break;
                }
            }
            if(!ok) printf("Abuchulaile
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    POJ3693 Maximum repetition substring —— 后缀数组 重复次数最多的连续重复子串
    SPOJ
    POJ2774 Long Long Message —— 后缀数组 两字符串的最长公共子串
    POJ3261 Milk Patterns —— 后缀数组 出现k次且可重叠的最长子串
    POJ1743 Musical Theme —— 后缀数组 重复出现且不重叠的最长子串
    SPOJ
    AC自动机小结
    HDU3247 Resource Archiver —— AC自动机 + BFS最短路 + 状压DP
    POJ1625 Censored! —— AC自动机 + DP + 大数
    Herding
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/15072196.html
Copyright © 2011-2022 走看看