• 周五. 3月 29th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

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

admin

11月 28, 2021

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;
}

《2021“MINIEYE杯”中国大学生算法设计超级联赛2》有一个想法
  1. Wow, incredible blog layout! How lengthy have you ever been running
    a blog for? you make running a blog look easy. The full look of your
    website is excellent, as neatly as the content material!
    You can see similar here e-commerce

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注