SMU Summer 2024 Contest Round 3

[ABC202E] Count Descendants - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 思路:在dfs序中每个点进入的时间Tin,和每个点离开的时间Tout都是不同的.
那么在本题询问中给出两个参数:U和D,问有多少个距离根节点为D的点,并且点U在到根节点的路径中.根据时间戳可知,若X满足该条件,那么 TinU<=TinX && ToutU>=ToutX那么根据这个性质,可以把同一层的Tin存到vector中.因为timer是递增的,那么在记录到vector中的时候也是递增的.那么,在询问的时候可以在vector[D]中二分得到地址ToutU-TinU偏移量的值就是满足条件的个数

int n,q,timer=0;
int Tin[200005],Tout[200005];   Tin[i] 记录dfs时第一次进入i的时间.  Tout[i] 记录dfs时离开i的时间.
vector<int> vct[200005];
vector<int> edge[200005];
void dfs(int cur,int d){
    Tin[cur]=++timer;
    vct[d].emplace_back(Tin[cur]);
    for(auto v:edge[cur]) dfs(v,d+1);
    Tout[cur]=++timer;
}
新知识:时间戳Tin,Tout
在dfs序中每个点进入的时间Tin,和每个点离开的时间Tout都是不同的.
那么在本题询问中给出两个参数:U和D,问有多少个距离根节点为D的点,并且点U在到根节点的路径中.
根据时间戳可知,若X满足该条件,那么 TinU<=TinX && ToutU>=ToutX
那么根据这个性质,可以把同一层的Tin存到vector中.
因为timer是递增的,那么在记录到vector中的时候也是递增的.
那么,在询问的时候可以在vector[D]中二分得到ToutU-TinU的值就是满足条件的个数
Count Descendants
https://www.luogu.com.cn/problem/AT_abc202_e
void solve(){
    cin>>n;
    for(int i=2;i<=n;i++){
        int x; cin>>x;
        edge[x].emplace_back(i);
    }
    dfs(1,0);
    cin>>q;
    while(q--){
        int u,d; cin>>u>>d;
        要lower_bound,因为查询的点可能是端点.
        这里通过 地址 - 地址, 得到的偏移量就是满足的个数
        cout<<lower_bound(vct[d].begin(),vct[d].end(),Tout[u])-lower_bound(vct[d].begin(),vct[d].end(),Tin[u])<<endl;
    }
}

 [ABC200D] Happy Birthday! 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:鸽巢原理..题意:在200个数字中,找不同的两组数字(长度不同or至少一个数字不同),使他们的和mod200相等. 思路:鸽巢原理:如果n个物体要放入m个抽屉中,且n>m,那么至少有一个抽屉中放有超过一个的物体。 key:若存在201个序列,那么一定其中有两个序列和对200取模相等。 对于n<=7,只需暴力枚举所有序列2^7=128,检查有没有重复的即可. 对于n>=8,是必然有解的,枚举到256个已经绰绰有余了。实际上201已经足矣. 时间复杂度o(mod)--只需要前log2(mod)+1个数字即可,后面再多数字也可以忽略不计!

int n;
int arr[250];
题意:在200个数字中,找不同的两组数字(长度不同or至少一个数字不同),使他们的和mod200相等.
思路:鸽巢原理:如果n个物体要放入m个抽屉中,且n>m,那么至少有一个抽屉中放有超过一个的物体。
key:若存在201个序列,那么一定其中有两个序列和对200取模相等。
对于n<=7,只需暴力枚举所有序列2^7=128,检查有没有重复的即可.
对于n>=8,是必然有解的,枚举到256个已经绰绰有余了。实际上201已经足矣.
时间复杂度o(mod)--只需要前log2(mod)+1个数字即可,后面再多数字也可以忽略不计!
Happy Birthday! 2
https://www.luogu.com.cn/problem/AT_abc200_d
void solve(){             补E
    cin>>n;
    n=min(8ll,n);       细节
    这一步是非常必要的,不然分类讨论n<=7和n>=8的话代码会非常冗余.
    如果想着取min的话,n太大会导致(1<<n)==0;所以干脆在这里取个min
    vector<int> ans[256];  201足矣
    unordered_map<int,int> mp;       mp1<余数,序列>
    for(int i=1;i<=n;i++) cin>>arr[i];
    for(int i=1;i<=(1<<n)-1;i++){
        int res=0;
        for(int j=0;j<min(8ll,n);j++){      这里取min
            if((i>>j)&1) {
                (res+=arr[j+1])%=200;
                ans[i].emplace_back(j+1);
            }
        }
        if(!mp[res]) mp[res]=i;
        else{
            cout<<"Yes"<<endl;
            int a1=mp[res],a2=i;
            sort(ans[a1].begin(),ans[a1].end());
            sort(ans[a2].begin(),ans[a2].end());
            cout<<ans[a1].size()<<" ";
            for(auto a:ans[a1]) cout<<a<<" ";
            cout<<endl;
            cout<<ans[a2].size()<<" ";
            for(auto a:ans[a2]) cout<<a<<" ";
            cout<<endl;
            return;
        }
    }
    cout<<"No";
}

[ABC204E] Rush Hour 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:最短路变种,推导一下公式可以得到经过一条边的最短时间是什么.正常跑dijkstra即可.

const int N=100005;
const int inf=LONG_LONG_MAX/2;
typedef struct node{
    int to,c,d;
};
int n,m;
priority_queue<pair<int,int>> pq;
vector<node> vct[N];
int dis[N];
bool vis[N];
void dijkstra(int s){
    for(int i=1;i<=n;i++) dis[i]=inf;
    dis[s]=0;
    pq.emplace(0,s);
    while(pq.size()){
        int from=pq.top().second;
        pq.pop();
        if(vis[from]) continue;
        vis[from]=1;
        for(auto v:vct[from]){
            int to=v.to,w;
            int C=v.c,D=v.d;
            Wmin=Ci-t-1+Di/(t0+t+1)+t0+t+1.
            配凑之后,令X=t0+t+1,其中t为当前时间,t0为停留时间
            则Wmin=Ci-t-1+Di/X+X
            其中Ci-t-1为常数,Di/X+X为对勾函数,在X=sqrt(Di)时取得最小值.
            但是要保证:出发时间(t0+t)>=当前时间dis[from],即sqrt(Di)-1>=dis[from]
            计算Wmin不能用二分,因为不是单调的。应该也许是三分。但是也可以直接算出来。
            虽然下面式子看着有点乱,但是只要一步一步推清楚上面的式子,是易懂的,不难的。
            因为是停留整数秒,那么X应该是整数的,但是sqrt(D)不一定是整数。
            严谨一点的话应该w的计算应该在代入floor(sqrt(D)),ceil(sqrt(D))中取min,而不是用round(sqrt(D))
            if(sqrt(D)-1>=dis[from]) w=min(C-dis[from]-1+D/floor(sqrt(D))+floor(sqrt(D)),C-dis[from]-1+D/ceil(sqrt(D))+ceil(sqrt(D)));
            else w=C+D/(dis[from]+1);
            if(dis[from]+w<dis[to]){
                dis[to]=dis[from]+w;
                pq.emplace(-dis[to],to);
            }
        }
    }
}
Rush Hour 2--小小配凑个式子,发现是对勾函数,代入最小值sqrt(D)
https://www.luogu.com.cn/problem/AT_abc204_e
void solve(){           G--最短路变种--有重边(正常使用)和自环(忽略)
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,c,d;
        cin>>u>>v>>c>>d;
        vct[u].emplace_back((node){v,c,d});
        vct[v].emplace_back((node){u,c,d});
    }
    dijkstra(1);
    if(dis[n]==LONG_LONG_MAX/2) cout<<"-1";
    else cout<<dis[n];
}

SMU 夏季 2024 比赛第 3 轮 - SMUOJ --- 搬寝室 - SMUOJ

思路:简单dp. dp[i][j]定义为考虑前i个物品,拿了j对物品,最小的疲劳值为dp[i][j].

dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+w); 当前状态由前两个转移来

int n,k;
int arr[2005];
int dp[2005][1005];   dp[i][j]定义为考虑前i个物品,拿了j对物品,最小的疲劳值为dp[i][j]
void init(){
    for(int i=0;i<=n;i++){      从0开始.
        for(int j=1;j<=k;j++){
            dp[i][j]=LONG_LONG_MAX/2;    因为后面进行了加运算,LONG_LONG_MAX会溢出;不确定INT_MAX够不够.
        }
    }
}
key:易知在有序列a,b,c,d中,最小的疲劳值为(a-b)^2+(c-d)^2..dp是建立在此基础上的.
搬寝室
void solve(){           补C--dp--o(n*k)
不难的dp,大概想到了dp数组的定义,但是只是死板的,还是不确定,没往下写
    while(cin>>n>>k){           逆天..是多组样例..题目没说。。
        init();
        for(int i=1;i<=n;i++) cin>>arr[i];
        sort(arr+1,arr+n+1);
        for(int i=2;i<=n;i++){
            int w=(arr[i]-arr[i-1])*(arr[i]-arr[i-1]);
            for(int j=1;j<=k;j++){
                if(2*j>i) break;        不合法的条件不进行下去
                dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+w);
            }
        }
        cout<<dp[n][k]<<endl;
    }
}

SMU 夏季 2024 比赛第 3 轮 - SMUOJ --- 寻找素数对 - SMUOJ

 思路:WA的有点奇怪的一题.原本写的是两for循环枚举两个质数。虽然暴力了点,但是不知道为什么会WA,可能是TLE报了WA. 改成枚举一个,然后另一个算出来检查是不是质数就A了.

const int maxn=1e6+10;
int primes[maxn+10],idx=0;
bool mark[maxn+10];
void get_primes(){
    for(int i=2;i<maxn;i++){
        if(!mark[i]) primes[++idx]=i;
        for(int j=1;j<=idx;j++){
            if(i*primes[j]>maxn) break;
            mark[i*primes[j]]=1;
            if(i%primes[j]==0) break;
        }
    }
}
void solve(){                   A--寻找素数对--????..
用两个for来依次枚举质数的话会wa。但是现在只枚举一个,另一个用mark[x-primes[i]]来确定又能ac。
    get_primes();
    //cout<<primes[idx];
    int x;
    while(cin>>x){
        int mindif=INT_MAX;
        pair<int,int> ans={0,0};
        for(int i=1;i<=idx;i++){
            if(primes[i]>x) break;
            int num=x-primes[i];
            if(!mark[num]&&abs(num-primes[i])<mindif){
                mindif=abs(x-primes[i]-primes[i]);
                ans={min(num,primes[i]),max(num,primes[i])};
            }
        }
        cout<<ans.first<<" "<<ans.second<<endl;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值