【寒假团队训练赛01】第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南)

本场总结

早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 AX。由于 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的(自由元)次方。
图1
图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);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值