191102-模拟测试11

191102-模拟测试11

T1 极好的问题

题目描述

T1

解析

考场上就只打了个30分的暴力 O ( n 3 ) O(n^3) ,但其实细想该题的套路还是很明显的,
首先枚举前两个数x,y的乘积,用exgcd求出所需z的值,便可将时间复杂度降至O(n^2),但有一些细节需要注意,比如判重等等(也可以分类讨论,分1,x,y,z都不同;2,两个相同;3,三个相同的情况)

暴力代码

#include<bits/stdc++.h>
using namespace std;
long long n,p,a[5009],ans,m,x,c[5009];
struct zb
{
	long long x,y,z;
}b[5000009];
bool comp(const zb &a,const zb &b)
{
	if(a.x!=b.x) return a.x<b.x;
	if(a.y!=b.y) return a.y<b.y;
	return a.z<b.z;
}
int main()
{
	freopen("awesome.in","r",stdin);
	freopen("awesome.out","w",stdout);
	scanf("%lld%lld",&n,&p);
	for(int i=1;i<=n;i++)
	{	
		scanf("%lld",&c[i]);
		a[i]=c[i]%p;
	}
	if(n<=100)
	{
		for(int i=1;i<=n;i++)
			for(int j=i+1;j<=n;j++)
				for(int k=j+1;k<=n;k++)
					if((((a[i]*a[j])%p)*a[k])%p==1)
					{
						ans++;
						b[ans].x=min(c[i],min(c[j],c[k]));
						b[ans].z=max(c[i],max(c[j],c[k]));
						b[ans].y=c[i]+c[j]+c[k]-b[ans].x-b[ans].z;
					}
		sort(b+1,b+ans+1,comp);
		m=ans;
		for(int i=1;i<=ans;i++)
			if(b[i].x==b[i-1].x&&b[i].y==b[i-1].y&&b[i].z==b[i-1].z)
				m--;
		printf("%lld",m);
		return 0;
	}
	return 0;
}

题解(照抄std的)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N=3000+10;

int P;
int fast_pow(int x,int y){
    int ret=1;
    while (y){
        if (y&1) ret=1LL*ret*x%P;
        x=1LL*x*x%P;
        y>>=1;
    }
    return ret;
}
struct Number{
    int val,inv;
    int cnt[4]={0,0,0,0};
} nums[N];
bool cmp_val(const Number& x,const Number& y){
    return x.val<y.val;
}
int n,m;
int a[N];
int find(int key){
    int l=-1,r=m,mid;
    while (l+1<r){
        mid=(l+r)>>1;
        if (nums[mid].val<key)
            l=mid;
        else
            r=mid;
    }
    if (r<m&&nums[r].val==key) return r;
    return -1;
}

int main(){
    scanf("%d%d",&n,&P);
    for (int i=0;i<n;++i)
        scanf("%d",&a[i]);
    sort(a,a+n);
    int stamp=0,tmp_cnt=1;
    for (int i=1;i<=n;++i){
        if (i==n||(i>0&&a[i]!=a[i-1])){
            int x=a[i-1]%P,j=0;
            if (x>0){
                while (j<stamp&&nums[j].val!=x)
                    ++j;
                if (j==stamp){
                    ++stamp;
                    nums[j].val=x;
                    nums[j].inv=fast_pow(x,P-2);
                }
                ++nums[j].cnt[min(3,tmp_cnt)];
            }
            tmp_cnt=1;
        }
        else ++tmp_cnt;
    }
    m=stamp;
    for (int i=0;i<m;++i){
        nums[i].cnt[2]+=nums[i].cnt[3];
        nums[i].cnt[1]+=nums[i].cnt[2];
        // printf("[ num ] %d %d (%d %d %d)\n",nums[i].val,nums[i].inv,nums[i].cnt[1],nums[i].cnt[2],nums[i].cnt[3]);
    }
    sort(nums,nums+m,cmp_val);
    int ans=0;
    for (int i=0;i<m;++i)
        for (int j=i;j<m;++j){
            int need=1LL*nums[i].inv*nums[j].inv%P;
            if (i!=j&&(need<=nums[i].val||need<=nums[j].val))
                continue;
            // if (need<nums[i].val||need<nums[j].val)
                // continue;
            // if (need==nums[i].val||need==nums[j].val)
                // if (nums[i].val!=nums[j].val)
                    // continue;
            int k=find(need);
            // printf("%d %d %d\n",i,j,k);
            if (k==-1)
                continue;
            int tmp=0;
            if (nums[i].val!=nums[j].val){ // 1,1,1
                tmp+=nums[i].cnt[1]*nums[j].cnt[1]*nums[k].cnt[1];
            }
            else if (nums[i].val!=nums[k].val){ // 2,1
                tmp+=nums[i].cnt[1]*(nums[i].cnt[1]-1)/2*nums[k].cnt[1];
                tmp+=nums[i].cnt[2]*nums[k].cnt[1];
            }
            else{ // 3
                tmp+=nums[i].cnt[3];
                tmp+=nums[i].cnt[2]*(nums[i].cnt[1]-1);
                tmp+=1LL*nums[i].cnt[1]*(nums[i].cnt[1]-1)*(nums[i].cnt[1]-2)/6;
            }
            // if (tmp) printf("%d %d %d\n",nums[i].val,nums[j].val,nums[k].val);
            ans+=tmp;
        }
    printf("%d\n",ans);
    return 0;
}

T2 bag

题目描述

T2

解析

考场上想出了正解,但在求最长不下降子序列的时候,只会 O ( n 2 ) O(n^2) 的算法,并且有一个细节没有取等,因此只拿了60分。
实际上,该题虽然有背包承重的限制,但我们可以很容易得到一个贪心算法,并且是具有正确性的,首先将物品按照重量升序排序,然后求出其价值的最长不下降子序列,并记录下每次转移的位置(树状数组实现 O ( n l o g n ) O(nlogn) ),然后我们按背包承重上限降序排序,每次二分答案出背包的数量num,自然选取前num个背包自然是最优的,然后判断每个物品对应的背包重量是否小于等于背包承重上限即可

考场代码

#include<bits/stdc++.h>//O(n^2)暴力70 
using namespace std;
struct zb
{
	int w,val;
}a[100009];
int w[100009],f[100009],g[100009],n,m,T;
int read()
{
	int pd=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-')
	{
		pd=-1;
		ch=getchar();
	}
	for(;isdigit(ch);ch=getchar())
		re=(re<<3)+(re<<1)+ch-'0';
	return re*pd;
}
bool comp(const zb &a,const zb &b)
{
	if(a.w!=b.w) return a.w<b.w;
	return a.val<b.val;
}
bool cmp(const int &a,const int &b)
{
	return a>b;
}
bool check(int r)
{
	int vis=0;
	for(int i=1;i<=n;i++)
		if(f[i]>=r)
		{
			vis=i;
			break;
		}
	if(!vis) return false;
	int cnt=1;
	bool bj=0;
	while(vis)
	{
		if(a[vis].w>w[cnt]){//注意不取等 
			bj=1;
			break;
		}
		vis=g[vis];
		cnt++;
	}
	if(bj) return false;
	return true;
}
int main()
{
	freopen("bag1.in","r",stdin);
	//freopen("bag.out","w",stdout);
	T=read();
	while(T--)
	{
		memset(a,0,sizeof(a));
		memset(w,0,sizeof(w));
		n=read();
		for(int i=1;i<=n;i++)
		{
			a[i].w=read();
			a[i].val=read();
		}
		sort(a+1,a+n+1,comp);
		m=read();
		for(int i=1;i<=m;i++)
			w[i]=read();
		sort(w+1,w+m+1,cmp);
		for(int i=1;i<=n;i++)
		{
			g[i]=0;
			f[i]=1;
		}
		for(int i=1;i<=n;i++)
		{
			int vis=0;
			int maxx=0;
			for(int j=i-1;j>=1;j--)
				if(a[i].val>=a[j].val)//注意取等 
					if(f[j]>=maxx)//注意取等 
					{
						maxx=f[j];
						vis=j;
					}
			f[i]+=maxx;
			g[i]=vis;
		}
		for(int i=1;i<=n;i++)
			printf("%d %d\n",f[i],g[i]);
		int l=1;
		int r=m;
		while(l<r)
		{
			int mid=(1+l+r)>>1;
			if(check(mid))
				l=mid;
			else
				r=mid-1;
		}
		printf("%d\n",l);
	}
	return 0;
}

正解(照抄std)

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> pii;
int n;

const int N=int(1e5)+10;

template<typename T>
struct BIT{
    T c[N];
    static int lowbit(int x){return x&-x;}
    void clear(){memset(c,0,sizeof(c));}
    void modify(int pos,T val){
        for (int i=pos;i<=n;i+=lowbit(i))
            c[i]=max(c[i],val);
    }
    T query(int pos){
        T ret=T();
        for (int i=pos;i;i-=lowbit(i))
            ret=max(ret,c[i]);
        return ret;
    }
};

BIT<int> bit;

int m;
pii item[N]; // first->weight ; second->value
#define weight first
#define value second
pii temp[N];
int bag[N];

void work(){
    scanf("%d",&n);
    for (int i=1;i<=n;++i){
        int w,v;
        scanf("%d%d",&w,&v);
        item[i]=make_pair(w,v);
    }
    sort(item+1,item+n+1);
    for (int i=1;i<=n;++i)
        temp[i]=make_pair(item[i].value,i);
    sort(temp+1,temp+n+1,greater<pii>());
    int stamp=0;
    for (int i=1;i<=n;++i){
        if (i==1||temp[i].first!=temp[i-1].first)
            ++stamp;
        item[temp[i].second].value=stamp;
    }
    scanf("%d",&m);
    for (int i=1;i<=m;++i){
        scanf("%d",&bag[i]);
    }
    sort(bag+1,bag+m+1,greater<int>());
    
    int p=0;
    bit.clear();
    int ans=0;
    for (int i=n;i>0;--i){
        while (p<m&&bag[p+1]>=item[i].weight) ++p;
        int res=bit.query(item[i].value);
        int nxt=min(res+1,p);
        bit.modify(item[i].value,nxt);
        ans=max(ans,nxt);
    }
    printf("%d\n",ans);
}

int main(){
    int T;
    scanf("%d",&T);
    while (T--){
        work();
    }
    return 0;
}

附上我自己写的假正解(每次会wa三个点)

#include<bits/stdc++.h>
using namespace std;
struct zb
{
	int w,val;
}a[1000009];
struct qrx
{
	int x,y;
}tree[3000009];
int w[1000009],f[1000009],g[1000009],n,m,T,len,c[1000009];
int read()
{
	int pd=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-')
	{
		pd=-1;
		ch=getchar();
	}
	for(;isdigit(ch);ch=getchar())
		re=(re<<3)+(re<<1)+ch-'0';
	return re*pd;
}
bool comp(zb a,zb b)
{
	if(a.w!=b.w) return a.w<b.w;
	return a.val<b.val;
}
bool cmp(int a,int b) {return a>b;}
int lowbit(int i) {return i&(-i);}
void update(int x,int y,int num)
{
	while(x<=len+1)
	{
		if(tree[x].x<y)
		{
			tree[x].x=y;
			tree[x].y=num;
		}
		x+=lowbit(x);
	}
}
qrx query(int x)
{
	qrx sum={0,0};
	while(x>0)
	{
		if(sum.x==tree[x].x)
			sum.y=min(sum.y,tree[x].y);
		if(sum.x<tree[x].x)
		{
			sum.x=tree[x].x;
			sum.y=tree[x].y;
		}
		x-=lowbit(x);
	}
	return sum;
}
bool check(int r)
{
	int vis=0;
	for(int i=1;i<=n;i++)
		if(f[i]>=r)
		{
			vis=i;
			break;
		}
	if(!vis) return false;
	int cnt=1;
	bool bj=0;
	while(vis)
	{
		if(a[vis].w>w[cnt]){//注意不取等 
			bj=1;
			break;
		}
		vis=g[vis];
		cnt++;
	}
	if(bj) return 0;
	return 1;
}
int main()
{
	freopen("bag2.in","r",stdin);
//	freopen("bag.out","w",stdout);
	T=read();
	while(T--)
	{
		memset(tree,0,sizeof(tree));
		memset(c,0,sizeof(c));
		memset(a,0,sizeof(a));
		memset(w,0,sizeof(w));
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		n=read();
		for(int i=1;i<=n;i++)
		{
			a[i].w=read();
			a[i].val=read();
			c[i]=a[i].val;
		}
		sort(a+1,a+n+1,comp);
		sort(c+1,c+n+1);
		len=unique(c+1,c+n+1)-c-1;
		m=read();
		for(int i=1;i<=m;i++)
			w[i]=read();
		sort(w+1,w+m+1,cmp);
		for(int i=1;i<=n;i++)
		{
			a[i].val=lower_bound(c+1,c+len+1,a[i].val)-c;
			qrx tmp=query(a[i].val);
			tmp.x+=1;
			f[i]=tmp.x;
			g[i]=tmp.y;
			update(a[i].val,tmp.x,i);
			//printf("%d %d %d %d\n",f[i],g[i],a[i].val,a[i].w);
		}
		int l=1;
		int r=m;
		while(l<r)
		{
			int mid=(1+l+r)>>1;
			if(check(mid))
				l=mid;
			else
				r=mid-1;
		}
		printf("%d\n",l);
	}
	return 0;
}

T3 子树问题

题目描述

T3

解析

考场上写的dfs暴力拿了28分,正解是计数类dp(着实不会
就先放篇题解吧 传送门

考场代码

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,k,l,r,ans,depth,a[10009],dep[30000],size[10009],fa[10009],vis[10009],x;
int read()
{
	int f=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-')
	{
		f=-1;
		ch=getchar();
	}
	for(;isdigit(ch);ch=getchar())
		re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
void getfa(int r,int add)
{
	size[r]+=add;
	if(fa[r]==r) return;
	getfa(fa[r],add);
}
void dfs1(int u)
{
	if(dep[u]>depth)
		return;
	if(u==n)
	{
		bool bj=0;
		for(int j=1;j<=n;j++)
			if(vis[size[j]])
				return;
		for(int j=1;j<=n;j++)
			if(dep[j]==depth)
			{
				bj=1;
				break;
			}
		if(bj) 
		{
			ans++;
			ans%=mod;
		}
		return;
	}
	for(int i=1;i<=u;i++)
	{
		dep[u+1]=dep[i]+1;
		fa[u+1]=i;
		getfa(u+1,1);
		dfs1(u+1);
		getfa(u+1,-1);
	}
}
void dfs2(int u)
{
	if(dep[u]>depth)
		return;
	if(u==n)
	{
		bool bj=0;
		for(int j=1;j<=n;j++)
			if(dep[j]==depth)
			{
				bj=1;
				break;
			}
		if(bj) 
		{
			ans++;
			ans%=mod;
		}
		return;
	}
	for(int i=1;i<=u;i++)
	{
		dep[u+1]=dep[i]+1;
		dfs2(u+1);
	}
}
int main()
{
	freopen("subtree.in","r",stdin);
	freopen("subtree.out","w",stdout);
	n=read();
	k=read();
	for(int i=1;i<=k;i++)
	{
		x=read();
		vis[x]=1;
	}
	l=read();
	r=read();
	if(vis[n])
	{
		for(int i=l;i<=r;i++)
			printf("0 ");
		 return 0;
	}
	if(k!=0)
	{

		for(int i=l;i<=r;i++)
		{
			depth=i;
			if(i==1)
			{
				printf("0 ");
				continue;
			}
			if(i==2)
			{
				if(vis[1])
					printf("0 ");
				else
					printf("1 ");
				continue;
			}
			memset(size,0,sizeof(size));
			memset(fa,0,sizeof(fa));
			memset(dep,0,sizeof(dep));
			ans=0;
			fa[1]=1;
			fa[2]=1;
			size[1]=2;
			size[2]=1;
			dep[1]=1;
			dep[2]=2;
			dfs1(2);
			printf("%d ",ans%mod);
		}
		return 0;
	}
	if(!k)
	{
		for(int i=l;i<=r;i++)
		{
			depth=i;
			if(i==1)
			{
				printf("0 ");
				continue;
			}
			if(i==2)
			{
				if(vis[1])
					printf("0 ");
				else
					printf("1 ");
				continue;
			}
			memset(dep,0,sizeof(dep));
			ans=0;
			dep[1]=1;
			dep[2]=2;
			dfs2(2);
			printf("%d ",ans%mod);
		}
		return 0;
	}
	return 0;
}

题解(照抄std)

#include <bits/stdc++.h>

using namespace std;

const int P=998244353;
const int N=500+10;
int c[N][N];

int n;

int dp[N][N];
bool ban[N];

int main(){
    int k;
    scanf("%d%d\n",&n,&k);
    memset(c,0,sizeof(c));
    for (int i=0;i<=n;++i){
        c[i][0]=1;
        for (int j=1;j<=i;++j)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%P;
    }
    memset(ban,0,sizeof(ban));
    for (int i=1;i<=k;++i){
        int x;
        scanf("%d",&x);
        ban[x]=1;
    }
    
    memset(dp,0,sizeof(dp));
    dp[1][1]=ban[1]?0:1;
    for (int d=2;d<=n;++d){
        for (int i=1;i<=n;++i){
            if (i==1){
                dp[i][d]=1;
                continue;
            }
            for (int j=1;j<i;++j){
                dp[i][d]=(dp[i][d]+1LL*dp[i-j][d]*dp[j][d-1]%P*c[i-2][j-1])%P;
            }
            // printf("dp[%d][%d]=%d\n",i,d,dp[i][d]);
        }
        for (int i=1;i<=n;++i)
            if (ban[i])
                dp[i][d]=0;
    }
    int L,R;
    scanf("%d%d",&L,&R);
    for (int i=L;i<=R;++i){
        printf("%d",(dp[n][i]-dp[n][i-1]+P)%P);
        if (i!=R) putchar(' ');
    }
    puts("");
    return 0;
}
发布了25 篇原创文章 · 获赞 2 · 访问量 661

猜你喜欢

转载自blog.csdn.net/Daniel__d/article/details/102880372