第十四届北航程序设计竞赛预赛题解(非官方)

 最近沙河好多人生病了,我也不能幸免,今天终于舒服了。补一补上周的老坑吧。
 接下来尽量每天刷题。另外要加油学习汇编语言,优化我们的密码学竞赛程序。

官方题解参见:http://clatisus.com/BCPC2018_qualification_round

A 两等分的Alice

https://buaacoding.cn/problem/1914/index
 非常容易设计出粗暴的dp。考虑 M [ i ] [ j ] M[i][j] M[i][j]表示当前考虑到第i个数字时,Alice的时间和ugo时间差为j时,单独练习的最小时间段数。更新无非就是三种方式,用 M [ i − 1 ] [ j ] M[i-1][j] M[i1][j]更新 M [ i ] [ j + a [ i ] ] M[i][j+a[i]] M[i][j+a[i]], M [ i ] [ j − a [ i ] ] M[i][j-a[i]] M[i][ja[i]],用 M [ i − 1 ] [ j ] + 1 M[i-1][j]+1 M[i1][j]+1更新 M [ i ] [ j ] M[i][j] M[i][j]。问题落在了j的数量级太大上,考虑到j虽然大,但是取值不多(这是因为b-a存在限制,可知每个数在区间 [ k ∗ a − 50 ∗ 50 , k ∗ a + 50 ∗ 50 ] [k*a-50*50,k*a+50*50] [ka5050,ka+5050]中,k取-50到50,所以对于i=50时,j总的数量的和不会超过5001*101=505101,而且这个估计很宽大了,实际上远小于这个值)。
 于是,考虑用map/unordered_map来代替dp数组记录值。这里unordered_map表现了更好的效率,另外采用滚动的方式能有效减小内存。
 注:官方题解采用了增加一维的方式而避免采用了库容器,有可能会更加高效。

#include<cstdio>
#include<unordered_map>
using namespace std;
using LL=long long;

unordered_map<LL,int> M[2];
int t[55],n,a,b,T,o;

int main()
{
	scanf("%d",&T);
	M[0][0]=1;
	while(T--)
	{
		scanf("%d%d%d",&n,&a,&b);
		for(int i=1;i<=n;i++)
			scanf("%d",&t[i]);
		o=0;
		M[o].clear();
		M[o][0]=1;
		for(int i=1;i<=n;i++)
		{
			o^=1;
			M[o].clear();
			for(auto &k:M[o^1])
			{
				int &t1=M[o][k.first+t[i]];
				t1=min(k.second,t1==0?100:t1);
				int &t2=M[o][k.first-t[i]];
				t2=min(k.second,t2==0?100:t2);
				int &t3=M[o][k.first];
				t3=min(k.second+1,t3==0?100:t3);
			}
		}
		printf("%d\n",M[o][0]-1);
	}
	return 0;
}

B 升级超梦

https://buaacoding.cn/problem/1917/index
 水题,考虑枚举级数x,检查的时候,选取费用最小的x-n-1个EXP,剩余的吃糖,看是否超出费用。正解说需要二分答案,但是数据范围很小,所以这里直接枚举。

#include<cstdio>
#include<algorithm>
using namespace std;

int n,m,a,b,c,s[105],T;

bool check(int x)
{
	for(int i=1;i<x;i++)
		s[i]=(i*a+b)%c;
	sort(s+1,s+x);
	int res=0;
	for(int i=1;i<x-n;i++)
		res+=s[i];
	return res<=m;
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
		int i;
		for(i=100;i>n;i--)
			if(check(i))
				break;
		printf("%d\n",i);
	}
	return 0;
}

C 小张的魔法跳越

https://buaacoding.cn/problem/1910/index
 看见异或第一反应一定要按位拆分,对于0起从低到高第i位,如果从x到y的边权的第i位中存在奇数个1,那么答案就要加上 x 到 y 边 权 和 ∗ 2 i x到y边权和*2^i xy2i。再考虑固定x为起点,所有的y的贡献用一次dfs可求,得 a n s x ans_x ansx,接着改变x,去O1更新 a n s x ans_x ansx,加入答案之中,也就是所谓的旋根操作。
 为了实现方便,我采用了黑白染色法,经过此位为1的边则子点和父点不同,否则相同。以1为首个x,第一次对1染白色,统计所有黑点的贡献以及每个子树中黑点的个数,同样旋根的时候也只以白点为起点,看黑点的贡献,第一次对1染黑色,统计所有白点的贡献以及每个子树中白点的个数,同样旋根的时候也只以黑点为起点,看白点的贡献。首次dfs记为mark函数,旋根操作为rot函数。
 不知道哪里写臭了,我卡常了,后来强硬地把mark函数改成非递归AC了,很不解。

#include<cstdio>
#include<cstring>
#define bit(a,i) (a>>i&1)
#define mo 1000000007
using namespace std;
using LL=long long;

struct edge
{
	int to;
	unsigned w;
	int nxt;
}E[200005];

struct item
{
	int o,x,fa;
	LL sum;
	int j;
}S[100005];

int T,n,x,y,cnt[100005],first[100005],en,p,top;
LL res,f[100005],t1;
unsigned z;

void mark(int o, int x, int fa, LL sum)
{
	top=0;
	int j;
	loop:
	for(j=first[x];j;j=E[j].nxt)
		if(E[j].to!=fa)
		{
			t1=sum+E[j].w;
			if(bit(E[j].w,p)^o)
			{
				cnt[E[j].to]=1;
				f[E[j].to]=t1;
			}
			S[top++]=(item){o,x,fa,sum,j};
			o^=bit(E[j].w,p),fa=x,x=E[j].to,sum=t1;
			goto loop;
			loop2:
			--top;
			o=S[top].o,fa=S[top].fa,x=S[top].x,sum=S[top].sum,j=S[top].j;
		}
	for(j=first[x];j;j=E[j].nxt)
		if(E[j].to!=fa)
			cnt[x]+=cnt[E[j].to],f[x]=(f[x]+f[E[j].to])%mo;
	if(top>0)
		goto loop2;
}

void rot(int o, int x, int fa, LL &ans, LL tmp, int c)
{
	if(c==o)
		ans=(ans+tmp)%mo;
	int j;
	for(j=first[x];j;j=E[j].nxt)
		if(E[j].to!=fa)
			rot(o,E[j].to,x,ans,(tmp+(LL)(cnt[1]-2*cnt[E[j].to]+mo)*E[j].w%mo+mo)%mo,c^bit(E[j].w,p));
}

LL solve()
{
	memset(f+1,0,n*sizeof(LL));
	memset(cnt+1,0,n*sizeof(int));
	mark(0,1,0,0);
	LL ans=0;
	rot(0,1,0,ans,f[1],0);
	memset(f+1,0,n*sizeof(LL));
	memset(cnt+1,0,n*sizeof(int));
	mark(1,1,0,0);
	cnt[1]++;
	rot(1,1,0,ans,f[1],0);
	return ans;
}

void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

void read(unsigned &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int main()
{
	read(T);
	while(T--)
	{
		read(n);
		memset(first+1,0,n*sizeof(int));
		en=0;
		for(int i=1;i<n;i++)
		{
			read(x),read(y),read(z);
			E[++en]=(edge){y,z,first[x]};
			first[x]=en;
			E[++en]=(edge){x,z,first[y]};
			first[y]=en;
		}
		res=0;
		for(int i=0;i<32;i++)
		{
			p=i;
			res=(res+solve()*(1LL<<i)%mo)%mo;
		}
		printf("%lld\n",res*(mo+1)/2%mo);
	}
	return 0;
}

D Bella 姐姐发辣条

https://buaacoding.cn/problem/1918/index
 水题不解释了,写个低效的代码玩玩

#include<cstdio>
using namespace std;

int a[105],b[105],T,n;

bool check(int x)
{
	int ret=x;
	for(int i=1;i<=n;i++)
	{
		ret+=b[i];
		if(ret<a[i])
			return false;
	}
	return true;
}

int sum(int x)
{
	int ret=x,res=0;
	for(int i=1;i<=n;i++)
		ret+=b[i],res+=ret;
	return res;
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)
			scanf("%d",&b[i]);
		int ans=0;
		while(!check(ans))
			++ans;
		printf("%d\n",sum(ans));
	}
	return 0;
}

E 禁忌的共鸣

https://buaacoding.cn/problem/1894/index
 考虑从大到小枚举gcd的值,找出所有gcd倍数的点,尝试连边,如果在同一个并查集里就不用连了,否则就连,加入并查集。其实就是克鲁斯卡尔算法的想法,效率的话是由一个数的因子个数均摊是logn的级别来限制住的(虽然只是均摊,关于一个数因子个数最多有多少个,没有固定的级别,但也不会远远超过logn)。

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
int T,n,fa[100005];
vector<int> h[100005],g;
long long ans;

int fis(int x)
{
	return fa[x]==x?x:fa[x]=fis(fa[x]);
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n); 
		for(int i=1E5;i>0;i--)
			h[i].clear();
		for(int i=1,a;i<=n;i++)
			scanf("%d",&a),h[a].push_back(i),fa[i]=i;
		ans=0;
		for(int i=1E5;i>0;i--)
		{
			for(int j=i;j<=1E5;j+=i)
				for(int k:h[j])
					g.push_back(k);
			for(int j=0;j<g.size();j++)
				if(fis(g[j])!=fis(g[0]))
					fa[fis(g[j])]=fis(g[0]),
					ans+=i;
			g.clear();
		}
		printf("%lld\n",ans);
	}	
	return 0;
}

F zzh 与同余方程

https://buaacoding.cn/problem/1908/index
 式子就抄一下官方题解
在这里插入图片描述
 筛完质数之后,接下来的话我用dp数组记录前一个求积,xdp数组记录后一个求积。埃氏筛就够了,不用欧拉筛。

#include<cstdio>
#define mo 998244353
using namespace std;
using LL=long long;

const int maxn=1E7+5;

int n,T,dp[maxn],xdp[maxn],ans[maxn];
bool vis[maxn];

void sieve(int n)
{
	for(int i=1;i<=n;i++)
		xdp[i]=dp[i]=1;
	for(int i=2;i*i<=n;i++)
		if(!vis[i])
			for(int j=i*i;j<=n;j+=i)
				vis[j]=true;
	LL j;
	for(int i=2,t;i<=n;i++)
		if(!vis[i])
		{
			for(j=i,t=1;j<=n;j*=i,t++);
			j/=i,t--;
			for(int h=j;h>=i;h/=i,t--)
				for(int k=h;k<=n;k+=h)
					if(k%((LL)h*i)!=0)
						dp[k]*=t+1,xdp[k]=(LL)xdp[k]*((k-1)*t+1)%mo;
		}
	for(int i=1;i<=n;i++)
		ans[i]=(ans[i-1]+(LL)xdp[i+1]*dp[i])%mo;
}

int main()
{
	scanf("%d",&T);
	sieve(1E7+1);
	while(T--)
	{
		scanf("%d",&n);
		printf("%d\n",ans[n]);
	}
	return 0; 
}

G 小z刷题

https://buaacoding.cn/problem/1912/index
 所有数据先离散化。首先明确一点,想维护k次方和,维护0~k-1次方和在所难免。第二,在求上升序列的时候,我们需要找到所有的前继点,用它们的数值和去更新这个节点的数值,这里意味着需要树状数组,所以开一个树状数组,节点是一个数组,第i位维护i次方和。更新的方式很容易推,是一个系数是杨辉三角的方程。
 数字出现多次不可怕,更新时顺便乘上次数即可。

#include<cstdio>
#include<algorithm>
#define mo 1000000007
using namespace std;
using LL=long long;

pair<int,int> p[100005];
int a[100005],y[100005],T,n,k,m,C[22][22];

struct item
{
	int d[22];
	item operator + (item &t)
	{
		item res;
		for(int i=0;i<=k;i++)
			res.d[i]=(d[i]+t.d[i])%mo;
		return res;
	}
}tmp,ttmp;

struct Finwick
{
	int n;
	item C[100005];
	
	void init(int x)
	{
		n=x;
		for(int i=1;i<=n;i++)
			for(int j=0;j<=k;j++)
				C[i].d[j]=0;
	}
	
	item sum(int x)
	{
		item res;
		for(int i=0;i<=k;i++)
			res.d[i]=0;
		while(x)
			res=res+C[x],x-=x&-x;
		return res;
	}
	
	void add(int x, item d)
	{
		while(x<=n)
			C[x]=C[x]+d,x+=x&-x;
	}
}F;

void C_init(int n)
{
	C[0][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=i;j++)
			C[i][j]=C[i-1][j]+(j>0?C[i-1][j-1]:0);
}

int main()
{
	scanf("%d",&T);
	C_init(20);
	while(T--)
	{
		scanf("%d%d",&n,&k);
		for(int i=1;i<=n;i++)
			scanf("%d%d",&p[i].first,&y[i]),p[i].second=i;
		sort(p+1,p+n+1);
		m=1;
		for(int i=1;i<=n;i++)
		{
			a[p[i].second]=m;
			if(i<n&&p[i].first!=p[i+1].first)
				m++;
		}
		F.init(m);
		for(int i=1;i<=n;i++)
		{
			ttmp=F.sum(a[i]-1);
			for(int j=0;j<=k;j++)
			{
				tmp.d[j]=ttmp.d[j]+1;
				for(int h=0;h<j;h++)
					tmp.d[j]=(tmp.d[j]+(LL)C[j][j-h]*ttmp.d[h])%mo;
			}
			for(int j=0;j<=k;j++)
				tmp.d[j]=(LL)tmp.d[j]*y[i]%mo;
			F.add(a[i],tmp);
		}
		ttmp=F.sum(m);
		printf("%d\n",ttmp.d[k]);
	}
	return 0;
}

H Draw 顺小王子

https://buaacoding.cn/problem/1916/index
 枚举题,枚举有可能抽到的两张牌,check是否组成顺子。没有坑点,不要粗心即可。

#include<cstdio>
#include<map>
#include<cstring>
using namespace std;

int T,pos[20];
char s[10];
map<char,int> M;

bool check()
{
	for(int i=1;i<=9;i++)
		if(pos[i]&&pos[i+1]&&pos[i+2]&&pos[i+3]&&pos[i+4])
			return true;
	if(pos[10]&&pos[11]&&pos[12]&&pos[13]&&pos[1])
		return true;
	return false;
}

int main()
{
	M['A']=1;
	for(int i=2;i<10;i++)
		M[i+'0']=i;
	M['T']=10;
	M['J']=11;
	M['Q']=12;
	M['K']=13;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s",s);
		memset(pos,0,sizeof(pos));
		for(int i=0;i<5;i++)
			pos[M[s[i]]]++;
		int ans=0;
		for(int i=1;i<=13;i++)
			for(int j=1;j<=13;j++)
			{
				pos[i]++;
				pos[j]++;
				if(pos[i]<=4&&pos[j]<=4)
					if(check())
						if(i!=j)
							ans+=(5-pos[i])*(5-pos[j]);
						else
							ans+=(6-pos[i])*(5-pos[i]);
				pos[i]--;
				pos[j]--;
			}
		printf("%d\n",ans/2);
	}
	return 0;
}

I Ange的DNA自动机

https://buaacoding.cn/problem/1898/index
 注意到这些矩阵的平方是单位阵,这意味着运算存在异或性,多次相乘同一矩阵,相当于乘模2次。再观察到某个元素第i步更新影响的元素排布满足异或杨辉三角。

0:1
1:11
2:101
3:1111
4:10001
5:110011
6:1010101
7:11111111

 由于第 2 i 2^i 2i行只涉及到2个元素,这意味着如果步数是2的幂,那么答案可以迅速求出。对任意步数,可以将步数k按位拆分,分logn次求即可。

#include<cstdio>
#include<algorithm>
#define bit(a,i) (a>>i&1)
using namespace std;

char s[2][1000005];
int T,n,k,o,p[128][128];

int main()
{
	scanf("%d",&T);
	p['A']['A']='A';
	p['A']['T']=p['T']['A']='T';
	p['A']['C']=p['C']['A']='C';
	p['A']['G']=p['G']['A']='G';
	p['C']['C']='A';
	p['C']['G']=p['G']['C']='T';
	p['C']['T']=p['T']['C']='G';
	p['G']['T']=p['T']['G']='C';
	p['G']['G']='A';
	p['T']['T']='A';
	while(T--)
	{
		scanf("%d%d%s",&n,&k,s[0]);
		o=0;
		for(int i=0;i<31;i++)
			if(bit(k,i))
			{
				for(int j=0;j<n;j++)
					s[o^1][j]=p[s[o][j]][s[o][(j+(1<<i))%n]];
				o^=1;
			}
		s[o][n]='\0';
		puts(s[o]);
	}
	return 0;
}

J 大丁的煤气灶

https://buaacoding.cn/problem/1896/index
 解出这题全靠数院的队友。首先用容斥原理,求出违反超过第 k 1 , k 2 , k 3 … k1,k2,k3… k1,k2,k3个小弟总数的方案(这里就是相当于转化为无上限的分割问题,可以用插板原理变成组合数),然后加加减减得到答案。
 大数组合数需要用到卢卡斯定理,现在还没完全领悟,网上抄的模板。然而还没完,模数是合数,需要用中国剩余定理对质因子模数答案进行组合。注意中国剩余定理过程中乘法数据范围可能超过long long,需要使用快速模乘黑科技。

#include<cstdio>
#include<algorithm>
using namespace std;
using LL=long long;

int n,T,k;
LL mo,f[20],m,p[20],q[20];

inline LL mul(LL a, LL b)
{
	return (a%mo)*(b%mo)%mo;
}

inline LL quick_mul(LL p, LL q, LL mo)
{
	p=(p%mo+mo)%mo,q=(q%mo+mo)%mo;
	return ((p*q-(LL)((long double)p/mo*q+1E-6)*mo)%mo+mo)%mo;
}

LL quick_power(LL a, LL b)
{
	LL res=1,base=a;
	while(b)
	{
		if(b&1)
			res=mul(res,base);
		base=mul(base,base);
		b>>=1;
	}
	return res;
}

LL C(LL a, LL b)
{
	if(a<b)
		return 0;
	b=min(a-b,b);
	LL u=1,d=1;
	for(LL i=0;i<b;i++)
	{
		u=mul(u,a-i);
		d=mul(d,i+1);
	}
	return mul(u,quick_power(d,mo-2));
}

LL lucas(LL a, LL b)
{
	return !b?1:mul(C(a%mo,b%mo),lucas(a/mo,b/mo));
}

LL solve()
{
	LL ans=0;
	for(int i=0;i<(1<<n);i++)
	{
		LL t=1,sum=m;
		for(int j=1;j<=n;j++)
			if(i>>j-1&1)
				sum-=f[j]+1,t*=-1;
		if(sum<0)
			continue;
		ans=(ans+t*lucas(sum+n-1,n-1)+mo)%mo;
	}
	return ans;
}

void gcd(LL a, LL b, LL &d, LL &x, LL &y)
{
	if(!b)
		d=a,x=1,y=0;
	else
		gcd(b,a%b,d,y,x),y-=x*(a/b);
}

LL china(LL n, LL *a, LL *m)
{
	LL M=1,d,y,x=0;
	for(int i=0;i<n;i++)
		M*=m[i];
	for(int i=0;i<n;i++)
	{
		LL w=M/m[i];
		gcd(m[i],w,d,d,y);
		x=(x+quick_mul(y*w,a[i],M))%M;
	}
	return (x+M)%M;
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%lld%d",&n,&m,&k);
		for(int i=1;i<=n;i++)
			scanf("%lld",&f[i]);
		for(int i=1;i<=k;i++)
		{
			scanf("%lld",&p[i]);
			mo=p[i];
			q[i]=solve();
		}
		printf("%lld\n",china(k,q+1,p+1));
	}
	return 0;
}

K wjj 的自动售货机

https://buaacoding.cn/problem/1911/index
 官方题解的链表/并查集是一个更优秀的选择,也更容易理解。这里给出线段树解法。
 一个数对比不比它大的数取模之后大小至少减到一半。所以在变0前,即使我们对每个数暴力修改,直到,效率依旧不会超过 n ∗ l o g M ∗ F n*logM*F nlogMF M表示数据大小,F是均摊的单次修改效率,此处不会超过logn,区间足够大时约为1。用线段树维护最小值以及区间和。另外注意到如果一个区间的数都是0不应该被修改,所以要记录一下区间最大值,否则会有大量无效的修改,使得效率难以得到保证。
 这么处理效率是有保证的,具体证明可以尝试绘画线段树结构图。

#include<cstdio>
#include<algorithm>
#define kl (k<<1)
#define kr (k<<1|1)
#define M (L+R>>1)
#define lin L,M
#define rin M+1,R
using namespace std;
using LL=long long;

struct node
{
	LL x,y,sum;
};

int t,n,q,l,r;
node T[1<<19];
LL mv;

void maintain(int k)
{
	T[k].x=min(T[kl].x,T[kr].x);
	T[k].y=max(T[kl].y,T[kr].y);
	T[k].sum=T[kl].sum+T[kr].sum;
}

void build_tree(int k, int L, int R)
{
	if(L==R)
	{
		scanf("%lld",&T[k].sum);
		T[k].x=T[k].y=T[k].sum;
		if(T[k].x==0)
			T[k].x=2E18;
		return ;
	}
	build_tree(kl,lin);
	build_tree(kr,rin);
	maintain(k);
}

LL query(int k, int L, int R)
{
	if(l<=L&&R<=r)
		return T[k].sum;
	LL res=0;
	if(l<=M)
		res+=query(kl,lin);
	if(r>M)
		res+=query(kr,rin);
	return res;
}

LL query_min(int k, int L, int R)
{
	if(l<=L&&R<=r)
		return T[k].x;
	LL res=2E18;
	if(l<=M)
		res=min(res,query_min(kl,lin));
	if(r>M)
		res=min(res,query_min(kr,rin));
	return res;
}

void modify(int k, int L, int R)
{
	if(T[k].y<mv)
		return ;
	if(L==R)
	{
		T[k].sum=T[k].x=T[k].y=T[k].sum%mv;
		if(T[k].x==0)
			T[k].x=2E18;
		return ;
	}
	if(l<=M)
		modify(kl,lin);
	if(r>M)
		modify(kr,rin);
	maintain(k);
}

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&q);
		build_tree(1,1,n);
		while(q--)
		{
			scanf("%d%d",&l,&r);
			printf("%lld ",query(1,1,n));
			mv=query_min(1,1,n);
			if(mv>0)
				modify(1,1,n);
			printf("%lld\n",query(1,1,n));
		}
	}
	return 0;
}

L Auto Chess

https://buaacoding.cn/problem/1895/index
 很容易发现固定轮数时,可以尽量用后面的棋子。于是想到二分答案。没什么大的坑点。我居然是因为变量读入顺序弄错导致WA了多次……

#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#define M (L+R>>1)
using namespace std;
using LL=long long;

int n,m,k,p,l,h[200005],sz,tmp[200005],tag[200005],T;
vector<int> V[200005];

bool check(int x)
{
	memset(tmp,0,sizeof(tmp));
	memset(tag,0,sizeof(tag));
	int tsz=0;
	for(int i=x;i>0;i--)
		for(int j=0;j<m;j++)
		{
			tmp[V[i][j]]++;
			if(tmp[V[i][j]]<=h[V[i][j]])
				tag[i]++;
			if(tmp[V[i][j]]==h[V[i][j]])
				tsz++;
		}
	if(tsz<sz)
		return false;
	LL money=0;
	for(int i=1;i<=x;i++)
	{
		money+=p+money/k;
		money=min(money,(LL)(1E18));
		money-=tag[i];
		if(money<0)
			return false;
	}
	return true;
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d%d",&n,&m,&p,&k,&l);
		for(int i=1;i<=n;i++)
		{
			V[i].clear();
			for(int j=1,a;j<=m;j++)
				scanf("%d",&a),V[i].push_back(a);
		}
		memset(h,0,sizeof(h));
		sz=0;
		for(int i=1,p;i<=l;i++)
		{
			scanf("%d",&p);
			if(++h[p]==1)
				++sz;
		}
		int L=1,R=n+1;
		while(L<R)
			if(!check(M))
				L=M+1;
			else
				R=M;
		printf("%d\n",L>n?-1:L);
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值