本场总结
早8:00开始,时长5h。
本次团队训练赛使用45th icpc济南赛站套题。vp平台:牛客
参赛队伍:5队、7队、13队,以及现场赛的1队、4队、8队
这一场从4题开始,难度直接飞升。
1队几乎同时连着过了A(高斯消元)和L(数位dp),从4AC一跃到6AC,进入金牌区前段,排名13。
截止3.5h,7队过了L的数位dp,我们过了A的高斯消元。dp一直是我们队的弱项,我也在努力学习dp。
截止4h,7队也过了A,排名14。
比赛链接
若是我过的题,下面会有解析。
M. Cook Pancakes!
第一题过的是这个,应该是签到题。
过题者:tqz
WA了一发
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
LL shang(LL n, LL x)
{
if (n % x == 0)
{
return n / x;
}else
{
return n / x + 1;
}
}
int main()
{
LL n, k;
scanf("%lld %lld", &n, &k);
printf("%lld\n",max((LL)2, shang(2 * n, k)));
// cin >> n;
}
G. Xor Transformation
第二发过了这个。
我过的,所以有解析。
题意:
需要使用异或操作把
X
X
X变成
Y
Y
Y。
需要注意的是,每次异或的数
A
A
A必须满足
A
≤
X
A\le X
A≤X。由于
X
X
X异或后发生了变化,所以
A
A
A也范围也会随之变化。
请在5次以内把
X
X
X变成
Y
Y
Y。输出次数和每次用来异或的
A
A
A的值。
前置知识:异或时,自己异或自己是1。
题解:
实际上,每次要么一次,要么两次,就可以把按题目要求把
X
X
X变成
Y
Y
Y。
①若
X
X
X ^
Y
Y
Y本身就
≤
X
\le X
≤X,那就直接输出
X
X
X ^
Y
Y
Y,一次就够,因为
X
X
X ^ (
X
X
X ^
Y
Y
Y) =
Y
Y
Y成立。
②若
X
X
X ^
Y
>
X
Y> X
Y>X,那需要两次,输出
Y
Y
Y和
X
X
X即可。因为题目保证了
Y
<
X
Y<X
Y<X。为什么成立呢?因为其实还是相当于
X
X
X ^
X
X
X ^
Y
Y
Y这三个数,只不过顺序变一变而已。
int main()
{
ll x,y;
cin >> x >> y;
if((x^y) <= x)
{
cout << "1\n" << (x^y);
}
else
{
cout << "2\n" << y << " " << x;
}
}
C. Stone Game
这是过的第三道题。
过题者:tqz
#include <bits/stdc++.h>
#define sz(v) (int)v.size()
#define debug(var) cout << #var << ": " << var << endl;
using namespace std;
using ll = long long;
using pii = pair<int, int>;
int main() {
ll a1, a2, a3;
cin >> a1 >> a2 >> a3;
ll wxl = min(a1, a2);
a1 -= wxl, a2 -= wxl;
ll xwf1 = a1 / 3, xwf2 = a2 / 3;
a1 -= xwf1 * 3, a2 -= xwf2 * 3;
ll ans = wxl * 2 + xwf1 * 3 + xwf2 * 6;
if (a1 == 2)
ans += 1;
if (a2 == 2)
ans += 4;
cout << ans;
}
D. Fight against involution
第四道过的题。
过题者:tqz
虽然不是我过的题,但这个题我也看了,所以可以解析一下,是个贪心。
题意:
每一个学生在写论文,每个人论文字数都有个区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri],若按照区间右端点
r
i
r_i
ri来排序,则每一个人都有一个成绩,成绩是
n
n
n减去字数高于
r
i
r_i
ri的人的个数。
但是大家都不想多写字,所以决定在保证排名不变的前提下,去减少论文字数,问总字数最少是多少。
题解(没有按照队友的代码来叙述,可能会有细微差异但大致相同):
其实就是优先按照右端点排序,右端点相同时再按照左端点排序。
扫一遍,先扫右端点小的,只需保证他们每个人写的最终字数是升序即可。累加得答案。
#include <bits/stdc++.h>
#define sz(v) (int)v.size()
#define debug(var) cout << #var << ": " << var << endl;
using namespace std;
using ll = long long;
using pii = pair<int, int>;
struct student {
ll l, r;
friend bool operator<(student a, student b) {
if (a.r != b.r) return a.r > b.r;
return a.l < b.l;
}
};
int main() {
int n;
cin >> n;
vector<student> a(n);
for (auto &e : a) cin >> e.l >> e.r;
sort(a.begin(), a.end());
ll ans = 0;
ll score = 0;
for (int i = n - 1; i >= 0; i--) {
score = max(score, a[i].l);
ans += score;
}
cout << ans;
}
A. Matrix Equation
第五道过的题。
难度突然加大,这题我过的,但是没有用
b
i
t
s
e
t
bitset
bitset优化居然也过了,复杂度
O
(
n
4
)
O(n^4)
O(n4),大概1.6e9的复杂度。
t
a
g
tag
tag:高斯消元求解异或方程组。
可以直接结合这两篇题解以及我画的图看:
1.题解
2.超全模板,可惜没有bitset优化版本
要注意
1.使用高斯消元的条件:独立条件是什么。
如这题把C矩阵每一列单独拉出来,每一列直接是相互独立的。
2.观察答案发现都是2的幂次,所以猜测答案为2的(自由元)次方。
我丑陋的代码
const int N = 210;
//高斯消元求解异或方程组/
/下标请从0开始//
int a[N][N];//增广矩阵//为01矩阵时可以使用bitset优化,复杂度变为O(n^3 / w),(w = 32 / 64)
int x[N];//解集
int freeX[N];//自由变元
int Gauss(int equ,int var){//返回自由变元个数
/*初始化*/
for(int i=0;i<=var;i++){
x[i]=0;
freeX[i]=0;
}
/*转换为阶梯阵*/
int col=0;//当前处理的列
int num=0;//自由变元的序号
int row;//当前处理的行
for(row=0;row<equ&&col<var;row++,col++){//枚举当前处理的行
int maxRow=row;//当前列绝对值最大的行
for(int i=row+1;i<equ;i++){//寻找当前列绝对值最大的行
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row){//与第row行交换
for(int j=row;j<var+1;j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0){//col列第row行以下全是0,处理当前行的下一列
freeX[num++]=col;//记录自由变元
row--;
continue;
}
for(int i=row+1;i<equ;i++){
if(a[i][col]!=0){
for(int j=col;j<var+1;j++){//对于下面出现该列中有1的行,需要把1消掉
a[i][j]^=a[row][j];
}
}
}
}
/*求解*/
//无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
for(int i=row;i<equ;i++)
if(a[i][col]!=0)
return -1;
//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
int temp=var-row;//自由变元有var-row个
if(row<var)//返回自由变元数
return temp;
//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
for(int i=var-1;i>=0;i--){//计算解集
x[i]=a[i][var];
for(int j=i+1;j<var;j++)
x[i]^=(a[i][j]&&x[j]);
}
return 0;
}
int enumFreeX(int freeNum,int var){//枚举自由元,统计有解情况下1最少的个数
int sta=(1<<(freeNum));//自由元的状态总数
int res=INF;
for(int i=0;i<sta;++i){//枚举状态
int cnt=0;
for(int j=0;j<freeNum;j++){//枚举自由元
if(i&(1<<j)){
cnt++;
x[freeX[j]]=1;
}else
x[freeX[j]]=0;
}
for(int k=var-freeNum-1;k>=0;k--){//没有自由元的最下面一行
int index=0;
for(index=k;k<var;index++){//在当前行找到第一个非0自由元
if(a[k][index])
break;
}
x[index]=a[k][var];
for(int j=index+1;j<var;++j){//向后依次计算出结果
if(a[k][j])
x[index]^=x[j];
}
cnt+=x[index];//若结果为1,则进行统计
}
res=min(res,cnt);
}
return res;
}
int qpow(int a,int b)
{
int res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
/*
int main(){
memset(a,0,sizeof(a));
int equ,var;
scanf("%d%d",&equ,&var);
for(int i=0;i<equ;i++){//构造初始状态
}
for(int i=0;i<equ;i++)//最终状态
scanf("%d",&a[i][var]);
int freeNum=Gauss(equ,var);//获取自由元
if(freeNum==-1)//无解
printf("inf\n");
else if(freeNum==0){//唯一解
int res=0;
for(int i=0;i<var;i++)
res+=x[i];
printf("%d\n",res);
}
else{//多个解
int res=enumFreeX(freeNum,var);
printf("%d\n",res);
}
return 0;
}
*/
int A[N][N],B[N][N];
signed main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
IOS;
int n;
cin >> n;
rep(i,0,n-1)
{
rep(j,0,n-1)
{
cin >> A[i][j];
}
}
rep(i,0,n-1)
{
rep(j,0,n-1)
{
cin >> B[i][j];
}
}
int ans = 1;
rep(col,0,n-1)
{
rep(i,0,n-1)//每一列都高斯一遍求解,所以需先初始化一下a
{
rep(j,0,n-1)
{
a[i][j] = A[i][j];
}
}
rep(i,0,n-1) a[i][i] = (A[i][i] - B[i][col] + 2) % 2;
int zyy = Gauss(n,n);
if(zyy == -1) continue;//没有自由元,无贡献
ans = ans * qpow(2,zyy) % mod;
}
cout << ans;
}
L. Bit Sequence
距离比赛还剩5分钟的时候过掉的。
过题者:tqz
t
a
g
tag
tag: 数位dp
#include <bits/stdc++.h>
#define sz(v) (int)v.size()
#define debug(var) cout << #var << ": " << var << endl;
using namespace std;
using ll = long long;
using pii = pair<int, int>;
int f(int x) { return __builtin_parity(x); }
int m;
ll L;
vector<int> a;
ll calc(int lim, int sum, int cc) {
int res = 0;
int hi = lim ? L % (1 << 7) : (1 << 7) - 1;
for (int i = 0; i <= hi; i++) {
int flag = 1;
for (int j = 0; j < m && flag; j++)
if (i + j < (1 << 7))
flag &= (f(i + j) ^ sum) == a[j];
else
flag &= (f(i + j) ^ sum ^ cc) == a[j];
res += flag;
}
return res;
}
ll memo[64][2][2][2];
ll dfs(int pos, int lim, int sum, int cc) {
ll &res = memo[pos][lim][sum][cc];
if (res != -1)
return res;
if (pos < 7)
return res = calc(lim, sum, cc);
res = 0;
int up = lim ? (L >> pos & 1) : 1;
for (int i = 0; i <= up; i++)
res +=
dfs(pos - 1, lim && i == up, sum ^ i, i & !cc);
return res;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
// int m;
// ll L;
scanf("%d%lld", &m, &L);
a = vector<int>(m);
for (auto &e : a)
scanf("%d", &e);
memset(memo, -1, sizeof(memo));
int len = 0;
ll ans = dfs(60, 1, 0, 0);
printf("%lld\n", ans);
}
}