2021中超第二场补题

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

1011

(1)没什么用的小知识

x & -x
在&操作中 x x x − x -x x是通过补码来表示的,故x & -x = x & (~x + 1)

x x x为偶数的时候,偶数取反后最后一位是1,在+1后就会进位,直到有一位由0变1,相与后只有那一位为1,所以结果是2的幂。

x x x为奇数的时候,最后一位是0,在+1后就不会会进位,相与后为1。

(2)思路

因为 i & j ≥ k i\&j≥k i&jk 所以 m i n ( i , j ) ≥ k min(i,j)≥k min(i,j)k ,故 i ≥ k i≥k ik并且 j ≥ k j≥k jk
这里可以用动态规划的思想, A [ i ] = A[i]= A[i]= 二进制i为1的位一定为1 其他位可能唯1也可能为0的 A数组中数中的最大值。
例如 i = 100 i=100 i=100则它可以由101、110、111通过&操作得到。 A [ 101 ] = m a x ( A [ 101 ] , A [ 111 ] ) , A [ 100 ] = m a x ( A [ 100 ] , A [ 101 ] , A [ 110 ] ) A[101]=max(A[101],A[111]),A[100]=max(A[100],A[101],A[110]) A[101]=max(A[101],A[111]),A[100]=max(A[100],A[101],A[110])
举个栗子: i = 101001 i=101001 i=101001则它为 m a x ( A [ 101001 ] , A [ 111001 ] , A [ 101101 ] , A [ 101011 ] ) max(A[101001],A[111001],A[101101],A[101011]) max(A[101001],A[111001],A[101101],A[101011])
依次类推 B [ i ] = B[i]= B[i]= 二进制i为1的位为1其他位可能唯一的B数组中数中的最大值, a [ i ] = a[i]= a[i]= 二进制i为1的位为1其他位可能唯一的A数组中数中的最小值
最后维护ans[i]输出答案

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6+10;
const ll mod = 998244353;
const int inf = 1e9+7;
int A[N],B[N],a[N],b[N];
ll ans[N];
int main(){
	ios::sync_with_stdio(false); cin.tie(0);
	int T,n;
	cin >> T;
	while(T--){
		cin >> n;
		for(int i = 0;i<n;i++){
			cin >> A[i]; a[i] = A[i];
		}
		for(int i = 0;i<n;i++){
			cin >> B[i]; b[i] = B[i];
		}
		int m = 1;
		while(m<n) m <<= 1;
		for(int i = n;i<=m;i++){
			A[i] = -inf;
			B[i] = -inf;
			a[i] = inf;
			b[i] = inf;
		}
		for(int i = n-1;i>=0;i--){
			for(int j = 1;j<m;j <<= 1){
				if(!(i&j)){
					A[i] = max(A[i],A[i^j]);
					B[i] = max(B[i],B[i^j]);
					a[i] = min(a[i],a[i^j]);
					b[i] = min(b[i],b[i^j]);
				}
			}
		}
		ans[n] = -(ll)inf*inf;//可能超过1e18 
		for(int i = n-1;i>=0;i--){
			ans[i] = -(ll)inf*inf;
			ans[i] = max(ans[i],(ll)A[i]*B[i]);
			ans[i] = max(ans[i],(ll)A[i]*b[i]);
			ans[i] = max(ans[i],(ll)a[i]*B[i]);
			ans[i] = max(ans[i],(ll)a[i]*b[i]);
			ans[i] = max(ans[i],ans[i+1]);
		}
		ll res = 0;
		for(int i = 0;i<n;i++){
			res = (res+ans[i])%mod;
		}
		res = (res+mod)%mod; /*!!!!ans[i]可能是负的*/
		cout << res << endl;
	}
	return 0;
}

1010

有用的小知识

引理1:对于任意整数 a , b , c a,b,c a,b,c,若 m m m c c c互质,则若 a . c % m = b . c a.c\%m=b.c a.c%m=b.c,则有 a % m = b a\%m=b a%m=b
证明: a . c % m = b . c a.c\%m=b.c a.c%m=b.c
= > ( a . c − b . c ) % m = 0 =>(a.c-b.c)\%m=0 =>(a.cb.c)%m=0
= > ( a − b ) . c % m = 0 =>(a-b).c\%m=0 =>(ab).c%m=0
以 为 m 和 c 互 质 以为m和c互质 mc
= > ( a − b ) % m = 0 =>(a-b)\%m=0 =>(ab)%m=0
= > a % m = b =>a\%m=b =>a%m=b

引理2 m m m是一个整数, b b b是一个整数且与 m m m互质,则a[1],a[2],a[3],a[4],…a[m]是模m的一个{1,2,…m-1}的序列,{b.a[i]%m}也是{1,2,…m-1}的序列

费马小定理 a p − 1 % p = 1 a^{p-1}\%p=1 ap1%p=1 p p p是质数,整数 a a a不是 p p p的倍数)
(证明可百度)

由引理2可知 b x {b_x} bx也是一个 { 1 , 2... P − 1 } \{1,2...P-1\} {1,2...P1}的序列。
要想知道 b x {b_x} bx的逆序对数,就是要知道 ∏ 1 < j < i < P ( b i − b j ) \prod_{1<j<i<P}(b_i-b_j) 1<j<i<P(bibj)的符号,就是要知道 ∏ 1 < j < i < P ( a i % p − a j % p ) \prod_{1<j<i<P}(ai\%p-aj\%p) 1<j<i<P(ai%paj%p)的符号,若对每个数除以 i − j i-j ij就会得到纯符号位为 ∏ 1 < j < i < P ( a % p ) \prod_{1<j<i<P}(a\%p) 1<j<i<P(a%p)即为 a P ( P − 1 ) 2 % P a^{\frac{P(P-1)}{2}}\%P a2P(P1)%P,该数为1就是偶数个逆序对,-1则为奇数个逆序对
a P ( P − 1 ) 2 % P a^{\frac{P(P-1)}{2}}\%P a2P(P1)%P
< = > ( a P − 1 ) P − 1 2 % P . ( a P − 1 2 ) % P <=>(a^{P-1})^\frac{P-1}{2}\%P.(a^\frac{P-1}{2})\%P <=>(aP1)2P1%P.(a2P1)%P
由费马定理得 < = > ( a P − 1 2 ) % P <=>(a^\frac{P-1}{2})\%P <=>(a2P1)%P
所以只要 < = > ( a P − 1 2 ) % P <=>(a^\frac{P-1}{2})\%P <=>(a2P1)%P就能得出结论

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
typedef long long ll;
int T;
ll a,P;
__int128 qp(ll a, ll b) {
    __int128 ret = 1;
    while (b) {
        if (b & 1)
            ret = ret * (__int128)a % (__int128)P;
        a = (ll)((__int128)a * (__int128)a% (__int128)P);
        b >>= 1;
    }
    return ret;
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0);
	cin >> T;
	while(T--){
		cin >> a >> P;
		ll ans = qp(a,(P-1)/2)%P;
		if(ans == 1) cout << 0 << endl;
		else cout << 1 << endl;
	}
	return 0;
}

1008

01背包+分组背包
背包初始化为负无穷表示要恰好装满,初始化为0就只要小于j就行了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
map<string,int> mp;
struct node{
	int v,w;
};
vector<node> subject[55];
int score[55][600],dp[600][4];
int main(){ 
	int T;
	cin >> T;
	while(T--){
		int n,m;string s;
		cin >> n;
		for(int i = 1;i<=n;i++){
			cin >> s;
			mp[s] = i;
		}
		memset(score,0,sizeof(score));
		memset(dp,-0x3f3f3f3f,sizeof(dp));
		cin >> m;
		for(int i = 1;i<=m;i++){
			int w,v;
			cin >> s >> v >> w;
			subject[mp[s]].push_back({v,w});
		}
		int t,p;
		cin >> t >> p;
		for(int i = 1;i<=n;i++){
			for(auto item : subject[i]){
				for(int j = t;j>=item.w;j--){
					score[i][j] = min(100,max(score[i][j],score[i][j-item.w]+item.v));
				}
			}
		}
		dp[0][0] = 0;
		for(int i = 1;i<=n;i++){
			for(int j = t;j>=0;j--){
				for(int pp = p;pp>=0;pp--){
					dp[j][pp] = -0x3f3f3f3f; 

					//每一次都初始化为负无穷,因为如果不能在上一层的前继状态找到可行解那这种状态就无法继续下去

					for(int k = 0;k<=j;k++){
						if(score[i][k] >= 60) dp[j][pp] = max(dp[j][pp],dp[j-k][pp]+score[i][k]);
						else if(pp) {
							dp[j][pp] = max(dp[j][pp],dp[j-k][pp-1]+score[i][k]);
						}
						if(score[i][k] == 100) break;
					}
				}
			}
		}
		int ans = -1;
		for(int i = 0;i<=t;i++){
			for(int j = 0;j<=p;j++){
				ans = max(ans,dp[i][j]);
			}
		}
		cout << ans << endl;
		mp.clear();
		for(int i = 1;i<=n;i++) subject[i].clear();
	} 
	return 0;
}

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值