Day 1

1.转圈游戏

(circle.cpp/c/pas)

【问题描述】

n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏。按照顺时针方向给 n 个位置编号,从

0 到 n-1。最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,……,依此类推。

游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,……,依此类推,第n − m号位置上的小伙伴走到第 0 号位置,第 n-m+1 号位置上的小伙伴走到第 1 号位置,……,第 n-1 号位置上的小伙伴顺时针走到第 m-1 号位置。

现在,一共进行了 10^k 轮,请问 x 号小伙伴最后走到了第几号位置。

【输入】
输入文件名为 circle.in。
输入共 1 行,包含 4 个整数 n、m、k、x,每两个整数之间用一个空格隔开。

【输出】
输出文件名为 circle.out。
输出共 1 行,包含 1 个整数,表示 10k 轮后 x 号小伙伴所在的位置编号。

【输入样例】
10 3 4 5

【输出样例】
5

【数据说明】
对于 30%的数据,0 ≤k<≤6;
对于 80%的数据,0 ≤k≤ 10^7;
对于 100%的数据,1 ≤n<≤1,000,000,0 ≤m≤n ,1 ≤ x ≤ n,0 ≤k≤ 10^9。

思路:用到快速幂优化,背诵内容

#include<cstdio>

typedef long long LL;
using namespace std;

int n,m,k,x;


LL fuc(int p,int k){
    LL temp=p,s=1;
    while(k!=0){
        if(k&1) s=(s*(temp%n))%n;
        temp=(temp*temp)%n;
        k>>=1;
    }
    return s;
}

int main(){
//  freopen("circle.in","r",stdin);
//  freopen("circle.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&k,&x);
    printf("%I64d",(m*fuc(10,k)+x)%n);
    return 0;
}
//printf %I64d 去掉 LL去掉 则无错误 

火柴排队

(match.cpp/c/pas)

【问题描述】

涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:Σni=(ai−bi)2,其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。

每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最

小交换次数对 99,999,997 取模的结果。

【输入】输入文件为 match.in。

共三行,第一行包含一个整数 n,表示每盒中火柴的数目。

第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

【输出】输出文件为 match.out。

输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。

【输入输出样例 1】

4 1
2 3 1 4
3 2 1 4

【输入输出样例说明】

最小距离是 0,最少需要交换 1 次,比如:交换第 1 列的前 2 根火柴或者交换第 2 列的前 2 根火柴。

【输入输出样例 2】

4 2
1 3 4 2
1 7 2 4

【输入输出样例说明】

最小距离是 10,最少需要交换 2 次,比如:交换第 1 列的中间 2 根火柴的位置,再交换第 2 列中后 2 根火柴的位置。

【数据范围】
对于 10%的数据, 1 ≤ n ≤10;
对于 30%的数据,1 ≤ n ≤ 100;
对于 60%的数据,1 ≤ n ≤ 1,000;

思路:a序列里第几大的要对上b序列里第几大的,可用结构体使a、b序列重新对齐

//逆序对 归并排序 
//大对大,小对小 
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int a[100010],b[100010],temp[100010],aa[100010],bb[100010];

void qs(int *x,int *y,int l,int r){
    int i=l,j=r,m=x[(l+r)>>1];
    while(i<=j){
        while(x[i]<m)i++;
        while(x[j]>m)j--;
        if(i<=j){
            swap(x[i],x[j]);
            swap(y[i++],y[j--]);
        }
    }
    if(i<r) qs(x,y,i,r);
    if(j>l) qs(x,y,l,j);
}

long long merge(int *a,int l,int m,int r,int *b){
    int i=l,k=l,j=m+1;
    long long sum=0;
    memcpy(b+l,a+l,sizeof(int)*(r-l+1));
    while(i<=m&&j<=r){
        if(b[i]<=b[j])
        a[k++]=b[i++];
        else{
            a[k++]=b[j++];
            sum+=(m-i+1);
            sum%=99999997;
        }
    }
    while(i<=m) a[k++]=b[i++];
    while(j<=r) a[k++]=b[j++];
    return sum;
}

long long merge_sort(int *a,int l,int r,int *b){
    long long sum=0;
    if(l<r){
        int m=(l+r)>>1;
        sum+=merge_sort(a,l,m,b);
        sum+=merge_sort(a,m+1,r,b);
        sum+=merge(a,l,m,r,b);
        sum%=99999997;
    }
    return sum;
}
int main(){
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
    int n,i;
    long long sum;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]),
        aa[i]=i;
    for(i=1;i<=n;i++)
        scanf("%d",&b[i]),
        bb[i]=i;
    qs(a,aa,1,n);
    qs(b,bb,1,n);
    for(i=1;i<=n;i++)
        b[aa[i]]=bb[i];
    sum=merge_sort(b,1,n,temp);
    printf("%lld",sum);
    return 0;
}

或者

//树状数组 
#include<cstdio>
#include<algorithm>
#include<iostream> 
using namespace std;


const int siz=100010,mod=99999997;
int n,ans;
int an[siz],c[siz]; 


struct node{int v,i;} a[siz],b[siz];
bool cmp(const node &c,const node &d){return c.v<d.v;}
int lowbit(int x){return x&(-x);}
void add(int x,int y){for(;x<=n;x+=lowbit(x)) c[x]+=y;}
//加上去 
int get(int x){int res=0;for(;x;x-=lowbit(x)) res+=c[x];return res;}
//求前缀和 

int main(){
    ans=0; 
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i].v),a[i].i=i;
    for(int i=1;i<=n;i++)
        scanf("%d",&b[i].v),b[i].i=i;
    sort(a+1,a+1+n,cmp);sort(b+1,b+1+n,cmp);
    for(int i=1;i<=n;i++)
        an[a[i].i]=b[i].i; 
//把b里的顺序按照a里的改了 a[i]第几个 a[i].i这个位置的数的大小排位 
    for(int i=n;i>=1;i--){
        ans+=get(an[i]-1); 
        ans%=mod; 
        add(an[i],1);
    }
    cout<<ans<<endl;
    return 0;   
}

货车运输

(truck.cpp/c/pas)

【问题描述】

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

【输入】输入文件名为 truck.in。

输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道

路。

接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

【输出】输出文件名为 truck.out。

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

【输入样例】

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

【输出样例】
3
-1
3

【数据说明】

对于 30%的数据,0 ≤n≤1,000,0 ≤m≤ 10,000,0≤q≤1,000;对于 60%的数据,0 ≤n≤1,000,0 ≤m≤50,000,0 ≤q≤ 1,000;

对于 100%的数据,0 ≤n≤10,000,0 ≤m≤50,000,0 ≤q≤30,000,0 ≤ z ≤ 100,000。

思路:最大生成树+LCA.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
vector <int> ans1[10010];
int m,n,q;
int father[10010],s[30010],t[30010];
int ans[30010];
bool vist[30010];

struct node{
    int x,y,z;
}a[51000];

int getfa(int x)//找爹 
{
    if(father[x]==x) return x;
    return father[x]=getfa(father[x]);
}

bool jianb(int x,int y)//建边 
{
    int a=getfa(x),b=getfa(y);
    if(a!=b)
    {
        father[b]=a;
        return false;
    }
    return true;
}

int cmp(node xx,node yy)//根据边权的大小排序 
{
    return xx.z>yy.z;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        father[i]=i;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    sort(a+1,a+m+1,cmp);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
        scanf("%d%d",&s[i],&t[i]);
    for(int i=1;i<=m;i++)
    {
        if(getfa(a[i].x)!=getfa(a[i].y))
            jianb(a[i].x,a[i].y);
        if(i==m || i%100==0)
        {
            for(int j=1;j<=q;j++)
                if(vist[j]==0 && getfa(s[j])==getfa(t[j]))
                    vist[j]=1,
                    ans1[(i-1)/100].push_back(j);
        }
    }
    for(int i=1;i<=n;i++)
        father[i]=i;
    memset(ans,-1,sizeof(ans));//如果无法求解,则输出-1 
    for(int i=1;i<=m;i++)
    {
        if(getfa(a[i].x)!=getfa(a[i].y))//若无连接,则连起来 
           jianb(a[i].x,a[i].y);
        int num=(i-1)/100;
        for(int j=0;j<ans1[num].size();j++)
        {
            if(ans[ans1[num][j]]==-1&&getfa(s[ans1[num][j]])==getfa(t[ans1[num][j]]))
                ans[ans1[num][j]]=a[i].z;
        }
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    return 0;
} 

这里有大神的详注

<think>我们正在讨论的是不使用预处理语句(即不使用prepare和execute)来执行UPDATE查询。在PDO中,我们可以使用`exec()`方法或`query()`方法直接执行SQL语句。但是,请注意,直接执行SQL语句需要我们自己构建SQL字符串,这可能会引入SQL注入的风险。因此,如果使用这种方式,必须确保所有插入到SQL语句中的值都是安全的(例如,已经过适当的转义或验证)。 然而,用户提供的SQL语句中有参数(用?占位),这通常用于预处理语句。如果我们不使用预处理语句,就需要将这些参数值直接插入到SQL字符串中。因此,我们需要做以下几步: 1. 获取所有要更新的值(即原本绑定到?的值)。 2. 将这些值进行适当的转义(防止SQL注入)。 3. 构建完整的SQL字符串。 4. 使用`exec()`或`query()`执行。 但是,请注意,PDO提供了`quote()`方法用于转义字符串,但要注意它可能会在字符串周围添加单引号,所以对于非字符串类型(如整数)可能需要额外处理(或者确保在SQL语句中字符串值有引号,而数字则不需要)。 另外,由于用户提供的SQL语句中使用了?占位符,我们需要将这些占位符替换为实际的值,并确保字符串值被正确引用。 示例SQL语句: UPDATE ctb_eeee SET day1=?, day2=?, day3=?, day4=?, day5=?, day6=? WHERE project=? AND module=? AND eeee=? 假设我们有一个数组$data,包含day1day6的值,以及$project, $module, $eeee三个值。 步骤: 1. 创建一个数组,包含所有要更新的字段值(day1day6)和条件值(project, module, eeee),顺序与SQL中的?顺序一致。 2. 遍历这个数组,对每个值进行适当的转义和格式化(字符串需要加引号,数字可以不加,但为了安全,我们使用PDO::quote(),它会自动处理字符串,并返回带引号的字符串。对于数字,我们可以选择不转义,但为了统一,我们可以判断类型,或者全部用quote转义,但要注意quote转义后字符串有引号,所以SQL语句中原本的占位符在替换时,如果是字符串,替换后就是带引号的,而数字则会被转成字符串并带引号,这可能导致SQL错误(因为数字字段不应该有引号)。因此,我们需要知道每个参数的数据类型。 由于不确定每个字段的类型,这里有一个折中的办法:对于数字类型的字段,我们使用强制类型转换(如(int)),然后直接拼接到SQL中(因为数字是安全的,不需要引号)。对于字符串,使用PDO::quote()进行转义。 但是,我们不知道每个字段的类型,所以另一种方法是全部使用quote转义,然后替换。这样,数字字段也会被加上引号,但SQLite是弱类型,所以可能可以接受(但这不是好习惯)。 因此,最好的做法是知道每个字段的类型,然后分别处理。如果不知道,我们可以假设所有day字段都是数字,而条件字段(project, module, eeee)是字符串。但实际情况可能不同。 为了简化,我们假设所有值都是字符串,并使用PDO::quote()进行转义。这样生成的SQL语句中,每个值都被单引号括起来。在SQLite中,如果字段是数值类型,它会自动转换,但可能影响性能,也可能导致类型错误(如果字段是整数,但传入的字符串不能转换为整数,则可能出错)。 因此,我们根据用户的具体情况来处理。以下是一个示例代码: 假设: $day1, $day2, ... $day6 是数值(整数或浮点数),而$project, $module, $eeee是字符串。 那么我们可以这样构建: 注意:由于数值类型不需要引号,所以我们可以直接使用,但必须确保这些值是安全的(即不包含SQL代码)。如果这些数值来自用户输入,我们不能完全信任,但因为是数值,我们可以强制转换成数字类型(如intval, floatval)来确保安全。 步骤: 1. 将数值类型的字段转换为数字(整数或浮点数),这样它们就是安全的,可以直接拼接到SQL字符串中。 2. 对于字符串类型的字段,使用PDO::quote()进行转义。 例如: $day1 = (float)$Day1; // 或者根据实际情况用intval // 然后直接拼接,因为数字是安全的 但是,在SQL语句中,数字可以直接写,所以我们可以这样构建: 示例代码: ```php // 假设我们有以下变量 $day1 = 10; // 这些值可能是从用户输入获取的,但我们已经转换为数字 $day2 = 20; $day3 = 30; $day4 = 40; $day5 = 50; $day6 = 60; $project = "some project"; // 字符串 $module = "some module"; $eeee = "some eeee"; // 创建数据库连接 $db = new PDO('sqlite:yourdatabase.sqlite'); // 构建SET部分,将数值字段直接转换为字符串(注意:确保是数字,安全) $setClause = "day1=" . $day1 . ", " . "day2=" . $day2 . ", " . "day3=" . $day3 . ", " . "day4=" . $day4 . ", " . "day5=" . $day5 . ", " . "day6=" . $day6; // 构建WHERE条件,字符串需要转义 $whereClause = "project=" . $db->quote($project) . " AND " . "module=" . $db->quote($module) . " AND " . "eeee=" . $db->quote($eeee); $sql = "UPDATE ctb_eeee SET " . $setClause . " WHERE " . $whereClause; // 使用exec执行 $affected = $db->exec($sql); if ($affected === false) { // 处理错误 $error = $db->errorInfo(); echo "Error: " . $error[2]; } else { echo "Updated $affected rows"; } ``` 但是,如果day1等字段也可能是字符串(比如包含非数字字符),那么我们就不能直接转换为数字。这时,我们需要知道它们的数据类型。如果都是字符串,那么我们可以全部使用quote转义。 假设所有字段都是字符串,那么: ```php $setClause = "day1=" . $db->quote($day1) . ", " . "day2=" . $db->quote($day2) . ", " . "day3=" . $db->quote($day3) . ", " . "day4=" . $db->quote($day4) . ", " . "day5=" . $db->quote($day5) . ", " . "day6=" . $db->quote($day6); ``` 然后条件部分同样用quote。 所以,关键在于知道字段的类型。如果混合类型,就要分别处理。 另外,注意如果字段值为NULL,那么quote会将其转成空字符串(不是SQL的NULL)。所以,如果允许NULL值,我们需要特殊处理: 例如: $value = $db->quote($value); 如果$value是null,quote会返回一个空字符串(即两个单引号),但我们需要的是NULL(不带引号)。所以,我们可以: ```php function pdoValue($db, $value) { if ($value === null) { return 'NULL'; } return $db->quote($value); } ``` 然后使用这个函数来构建每个值。 总结:不使用预处理语句执行UPDATE查询,我们需要手动构建SQL字符串,并确保所有值都被正确转义和格式化,以避免SQL注入和语法错误。 但是,强烈建议使用预处理语句,因为它们更安全、更方便。只有在特殊情况下才使用直接执行。 现在,针对用户的具体SQL语句,我们可以这样写(假设所有值都是字符串,并且处理NULL的情况): ```php // 假设$data包含day1day6的值,以及$project, $module, $eeee $values = [ 'day1' => $Day1, 'day2' => $Day2, 'day3' => $Day3, 'day4' => $Day4, 'day5' => $Day5, 'day6' => $Day6 ]; $conditions = [ 'project' => $project, 'module' => $module, 'eeee' => $eeee ]; // 构建SET部分 $setParts = []; foreach ($values as $field => $value) { $setParts[] = "$field=" . ($value === null ? 'NULL' : $db->quote($value)); } $setClause = implode(', ', $setParts); // 构建WHERE条件 $whereParts = []; foreach ($conditions as $field => $value) { $whereParts[] = "$field=" . ($value === null ? 'NULL' : $db->quote($value)); } $whereClause = implode(' AND ', $whereParts); $sql = "UPDATE ctb_eeee SET $setClause WHERE $whereClause"; $affected = $db->exec($sql); // 检查错误... ``` 注意:如果字段是数值类型,并且我们想避免加引号,那么我们需要在循环中判断字段类型(比如根据字段名),但这样需要额外的类型信息。如果无法获得类型信息,且字段可能是数值类型,那么我们可以尝试判断$value是否是数字字符串(使用is_numeric),如果是,则不加引号直接使用(但要确保安全,因为数字字符串是安全的)。但注意,如果数字字符串包含非数字字符(如千位分隔符),则不能直接使用。 因此,最安全的方式还是使用预处理语句。 由于问题要求不使用预处理语句,我们提供了上述方法,但请谨慎使用,并确保数据的安全性。 最后,如果使用SQLite,还可以使用事务来批量执行,但这里只是更新操作,所以不需要。 相关问题: 1. 在PDO中,如何防止SQL注入攻击? 2. 使用PDO的exec()方法和预处理语句在性能上有什么差异? 3. 在SQLite中,如何处理NULL值? 4. 如何安全地构建动态SQL查询?</think>### 在 PHP 中不使用预处理语句执行 UPDATE 查询 是的,可以在 PHP 中不使用预处理语句执行 UPDATE 查询,但需注意**安全风险**。以下是两种替代方法及其实现: --- ### 方法 1:使用 `PDO::exec()` 直接执行 SQL 字符串 ```php // 构建完整的 SQL 字符串(注意安全风险!) $sql = sprintf( "UPDATE ctb_eeee SET day1='%s', day2='%s', day3='%s', day4='%s', day5='%s', day6='%s' WHERE project='%s' AND module='%s' AND eeee='%s'", $day1, $day2, $day3, $day4, $day5, $day6, $project, $module, $eeee ); // 直接执行 SQL $affectedRows = $db->exec($sql); if ($affectedRows === false) { echo "更新失败: " . implode(", ", $db->errorInfo()); } else { echo "成功更新 $affectedRows 行"; } ``` **注意事项**: 1. **SQL 注入风险**:所有值必须手动转义 ```php // 使用 PDO::quote() 转义每个值 $safeDay1 = $db->quote($day1); $safeProject = $db->quote($project); // ...其他变量同理 ``` 2. **类型处理**:数字类型值不应加引号 ```php // 假设 day1 是整数 $day1Part = is_numeric($day1) ? $day1 : $db->quote($day1); ``` --- ### 方法 2:使用 `PDO::query()` 执行完整 SQL ```php // 构建安全 SQL(包含转义) $sql = "UPDATE ctb_eeee SET day1=" . $db->quote($day1) . ", day2=" . $db->quote($day2) . ", day3=" . $db->quote($day3) . ", day4=" . $db->quote($day4) . ", day5=" . $db->quote($day5) . ", day6=" . $db->quote($day6) . " WHERE project=" . $db->quote($project) . " AND module=" . $db->quote($module) . " AND eeee=" . $db->quote($eeee); // 执行查询 $stmt = $db->query($sql); if ($stmt) { echo "更新成功"; } else { echo "错误: " . implode(", ", $db->errorInfo()); } ``` --- ### ⚠️ 关键安全警告 1. **必须手动转义所有用户输入值** 使用 `$db->quote($value)` 对每个变量进行转义 ```php // 危险示例(SQL 注入漏洞) $sql = "UPDATE ... WHERE project='$_GET[project]'"; // 安全做法 $sql = "UPDATE ... WHERE project=" . $db->quote($_GET['project']); ``` 2. **数字类型特殊处理** 数值类型不应加引号: ```php // 错误(导致 SQL 错误) $sql = "SET day1='" . $intValue . "'"; // 正确 $sql = "SET day1=" . (int)$intValue; ``` 3. **NULL 值处理** 需特殊处理 NULL 值: ```php $value = ($day1 === null) ? 'NULL' : $db->quote($day1); ``` --- ### 最佳实践建议 | **场景** | **推荐方法** | **原因** | |------------------------------|----------------------------|------------------------------| | 含动态参数 | 预处理语句 (`prepare`+`execute`) | 唯一可靠的防注入方式 | | 完全可信的静态数据 | `PDO::exec()` | 简洁高效 | | 需要结果集 | `PDO::query()` | 直接获取结果集 | | 批量操作 | 事务+预处理语句 | 保证原子性和性能[^1] | > **强烈建议**:除非是处理完全可信的内部数据,否则**始终使用预处理语句**。这是防止 SQL 注入的唯一可靠方法[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值