T1
题面
路灯照明
时间限制:1秒 内存限制:256M
题目描述
小可是一个相信光的人,他最近在研究光与距离的问题。
给定一个 2∗22∗2 的网格,每个网格都有一盏路灯,且都在格点上,即:四盏路灯的位置分别是左上角, 右上角,左下角,右下角。
路灯都是需要耗电的,且耗电量与亮度有关,如果一盏路灯的耗电量是 x ,则它可以为他所在的格子提供 x 的亮度,并且为他相邻的格子提供 ⌊2x⌋,为他对角的格子提供 ⌊4x⌋的亮度,其中⌊x⌋表示对 x 向下取整。
某一个格子的亮度为四盏路灯为他提供的亮度之和,例如 左上角的灯耗电量为 4, 右上角的灯耗电量为 7,右下角的灯耗电量为 8,左下角的灯耗电量为 0,那么 左上角这个格子的亮度就是 4+⌊72⌋+⌊74⌋+04+⌊27⌋+⌊47⌋+0 。
现在我们对四个格子的最低亮度提出了要求,我们想要让四个格子的亮度都达到标准。你可以将每一盏灯的耗电量调节为任何一个大于等于零的整数,为了省电, 你希望四盏灯的耗电量之和尽可能的小,请问四盏灯的最小耗电量之和是多小?
输入描述
输入四个整数:a,b,c,d ,分别表示左上、右上、左下、右下 四个格子要求的亮度之和。
输出描述
输出一行一个整数表示四盏灯的最小耗电量之和。
输入样例1
50 24 25 12
输出样例1
50
样例说明1
左上角的位置的灯耗电量设置为 50,其它三个位置设为 0。即可满足亮度要求
输入样例2
8 8 8 8
输出样例2
15
样例说明2
4 盏灯耗电量依次为 4 3 4 4
数据描述
对于20%的数据:1≤a,b,c,d≤50
对于70%的数据:1≤a,b,c,d≤400
对于100%的数据:1≤a,b,c,d≤1500
解题思路
70分部分:枚举a,b,c的亮度,计算出d收到的影响,从而推出d的亮度,可以达到O(n^3)的时间复杂度
code
#include<bits/stdc++.h>
#define MAXN 100
#define ull unsigned long long int
#define ll long long int
using namespace std;
int a,b,c,d;
long long aa,bb,cc,dd;
int ans=0x3f3f3f3f;
int main(){
freopen("lighting.in","r",stdin);
freopen("lighting.out","w",stdout);
scanf("%d%d%d%d",&a,&b,&c,&d);
aa=a,bb=b,cc=c,dd=d;
if(aa*bb*cc*dd<=10000000){
for(int i=0;i<=a;i++){
for(int j=0;j<=b;j++){
for(int k=0;k<=c;k++){
for(int o=0;o<=d;o++){
if(i+j/2+k/2+o/4>=a&&j+i/2+o/2+k/4>=b&&k+i/2+o/2+j/4>=c&&o+j/2+k/2+i/4>=d){
ans=min(ans,i+j+k+o);
}
}
}
}
}
}
else{
for(int i=0;i<=a;i++){
for(int j=0;j<=b;j++){
for(int k=0;k<=c;k++){
int o=d-(j/2+k/2+i/4);
if(i+j/2+k/2+o/4>=a&&j+i/2+o/2+k/4>=b&&k+i/2+o/2+j/4>=c&&o+j/2+k/2+i/4>=d){
ans=min(ans,i+j+k+o);
}
}
}
}
}
cout<<ans;
//i j
//k o
fclose(stdin);
fclose(stdout);
return 0;
}
100分部分
对总答案进行二分搜索,枚举a,d(对角线)的亮度,从而推出b的亮度大致范围,可以达到O(n^2logn)的时间复杂度
code
#include<bits/stdc++.h>
#define MAXN 100
#define ull unsigned long long int
#define ll long long int
using namespace std;
int a,b,c,d;
int ans=0x3f3f3f3f;
bool check(int mid){
for(int i=0;i<=a;i++){
for(int j=0;j<=d;j++){
int need=max(a-i-j/4,d-j-i/4);//ad,haishengduoshao
if((mid-i-j)/2<need){
continue;
}
int now=mid-i-j;//haishengduoshaokaofenpei
int bneed=max(0,b-i/2-j/2);
int cneed=max(0,c-i/2-j/2);
int bb=max(0,(bneed-now/4)*4/3);//gusuanqujian
for(int k=max(0,bb-5);k<=min(now,bb+5);k++){//meijub
if(k+(now-k)/4>=bneed&&k/4+now-k>=cneed){
return true;
}
}
}
}
return false;
}
int main(){
//freopen("lighting.in","r",stdin);
//freopen("lighting.out","w",stdout);
scanf("%d%d%d%d",&a,&b,&c,&d);
int l=0,r=a+b+c+d,ans=a+b+c+d;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
ans=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
cout<<ans;
//fclose(stdin);
//fclose(stdout);
return 0;
}
T2
题面
蚂蚁上树
时间限制:2秒 内存限制:256M
题目描述
小可很喜欢让别人爬, 于是他种了一棵树。然后在树上放了 n 只蚂蚁,看它们爬。
树上有 n 个点,编号是从 1 ~ n,令 1 号点为根节点,每个点上都放了一只蚂蚁。
除了1号点的蚂蚁以外,其他的蚂蚁都可以爬到它的父节点(当然也可以不爬,且只能爬一次)。
众所周知,蚂蚁是群居生物,每只蚂蚁都有一个快乐值 ai,如果某个节点只有一只蚂蚁的话,这个快乐值是不会起作用的,如果某个节点上有多只蚂蚁,它们的快乐值就会起作用,快乐值为当前节点上的蚂蚁的异或值, 问所 有方案的所有快乐值之和。
输入描述
第一行:输入一个正整数 n,表示树的点数 。
第二行:输入 n 个数,表示每个蚂蚁的快乐值。
第三行:输入 n-1 个数字,表示 2~n 号节点的父节点编号。
输出描述
输出一个答案对10e9+7取模后的结果。
输入样例
3
1 2 4
1 2
输出样例
12
样例解释
树呈一条链,二号结点的父亲是一号结点,三号结点的父亲是二号结点。1,2,3 号结点蚂蚁的属性值分别为 1、2、4。这些蚂蚁共有 4 种爬的方案:
方案 1:[{1,2},{ },{4}] 表示第一个结点有属性值为 1,2 的两只蚂蚁,第二个结点无蚂蚁,第三个结点有属性值为 4 的一只蚂蚁。本方案的快乐值为 1 ⊕ 2 = 3,其中 ⊕ 表示异或。
方案 2:[{1,2},{4},{ }],本方案的快乐值为 3。
方案 3:[{1},{2},{4}],没有结点上有大于等于两只蚂蚁,所以快乐值为 0。
方案 4:[{1},{2,4},{ }],快乐值为 2 ⊕ 4 = 6 所有方案的快乐值之和为 3 + 3 + 0 + 6 = 12。
输入样例
3
1 2 2
1 1
输出样例
7
样例解释
方案 1:[{1},{2},{2}]
方案 2:[{1,2},{2},{ }]
方案 3:[{1,2},{ },{2}]
方案 4:[{1,2,2},{ },{ }]
所有方案的快乐值之和为 0 + 3 + 3 + 1 = 7
数据描述
本题共10个测试点:
测试点1:1≤n≤20
测试点2~3:1≤n≤1000
测试点4:树是一条链
测试点5~6:树是二叉树
测试点7~10:树形态没有特殊限制,1≤n≤105,0≤ai≤109
解题思路
10分部分
对每个节点上移或否的状态进行二进制枚举,O(2^n)的时间复杂度
code
if(n<=20){
for(int i=0;i<=pt[n-1]-1;i++){
int tm=i;
cnt=0;
for(int j=0;j<=21;j++){
p[j]=0;
ans[j]=0;
fl[j]=0;
}
fl[1]=1;
ans[1]=a[1];
//cout<<"biaozhi ";
for(int j=2;j<=n;j++){
if(tm&1){
p[j]=1;
}
else{
p[j]=0;
}
//cout<<p[j]<<' ';
tm=(tm>>1);
}
// cout<<endl;
for(int j=2;j<=n;j++){
if(p[j]==1){
fl[f[j]]++;
//cout<<"p "<<p[j]<<' '<<fl[f[j]]<<endl;
ans[f[j]]=ans[f[j]]^a[j];
}
else{
fl[j]++;
//cout<<"p "<<p[j]<<' '<<fl[j]<<endl;
ans[j]=ans[j]^a[j];
}
}
for(int j=1;j<=n;j++){
//cout<<fl[j]<<endl;
if(fl[j]>1){
cnt+=ans[j];
}
}
//cout<<"cnt "<<cnt<<endl;
tcnt+=cnt;
}
}
100分部分
对于每一个二进制位,对其1,0状态及其子节点上移个数和01状态分类讨论,并同时对其他剩余节点进行排列组合计算答案数量,与答案相乘后相加即可
code
void dfs(int pos){
if(v[pos].size()==0){
return;
}
tcnt+=a[pos]^a[v[pos][0]];
tcnt+=a[pos]^a[v[pos][1]];
dfs(v[pos][0]);
dfs(v[pos][1]);
}
void(int u){
memset(num,0,sizeof num);
int tot=1;
split(a[u],num);
for(int v:e[u]){
dfs(v);
split(a[v],num);
tot++;
}
if(tot==1)
return;
long long temp,r=two[tot-1],p=two[n-tot];
if(u==1){
r=two[tot-2];
if(u!=1)
p=two[n-tot-1];
for(int i=0;i<30;i++){
if(num[i]){
temp=(1<<i);
long long xi=r;
if(u==1&&((a[u]>>i)&1)&&(num[i]==1))
xi=(xi*2)%mod;
ans=(ans+temp*xi%mod+mod)%mod;
}
}
ans=(ans+a[u]*p%mod+mod)%mod;
if(u!=1)
for(int v:e[u])
ans=(ans-a[v]*p%mod+mod)%mod;
}
T3
题面
AB串
时间限制:1秒 内存限制:512M
题目描述
你有 n 个字母 A,m 个字母 B,你可以将这些字母组成成为一个字符串,你需 要使得这个字符串的权值尽量大。现在我们以如下规则计算这个字符串的权值。
1.每有连续的 a 个 A,且下一个字母依旧是 A,则权值 +1。假设 a= 3,且 连续有 7 个 A,那么根据此规则,权值 +2。你可以理解一段长度为 cntA 的 A 所获得的权值为⌊acntA−1⌋,
2.每有连续的 b 个 B ,且下一个字母依旧是 B,则权值 +1。
3.上一个字母和当前字母不一样时,权值 +1。(第一个字母前面没有字母,也 会使得权值 +1,详见样例 1)
假设当前字母是 B,则至少需要有连续 c 个字母 B,下一个字母才可以切换成 A。字母 A 切换到字母 B 没有任何限制。
请问你能构造的字符串权值最大可能是多少?
输入描述
第一行输入一个正整数 t ,表示测试样例组数。
接下来 t 行:每行输入五个正整数 n,m,a,b,c,表述如题。
输出描述
对于每一组输入数据,输出一行一个正整数表示答案。
输入样例
6
1 1 1 1 1
5 4 3 3 2
5 5 3 3 2
3 9 3 3 3
7 3 3 5 8
4 7 2 8 5
输出样例
2
5
6
8
4
5
样例说明
样例 1 可以设计为 AB 或者 BA,第一个字母前面没有字母,初始使得权值为 1,第二个字母和第一个不一样,权值 +1,总权值为 2。
样例 2 可以设计为 ABBAAAABB,也可以设计成 ABBAAABBA,权值均为 5。
对于样例 3,可以设计为 ABBABBAAAB,权值为 6。
对于样例 4,可以设计为 ABBBBABBBBAB,权值为 8。
对于样例 5,可以设计为 AAAAAAABBB,权值为 4。
对于样例 6,可以设计为 AAABBBBBABB 。
数据描述
对于20%的数据:1≤t≤5,1≤n,m,a,b,c≤10
对于50%的数据:1≤t≤50,1≤n,m,a,b,c≤100
对于100%的数据:1≤t≤50,1≤n,m,a,b,c≤105
实在会不了一点,先放个代码
code
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/rope>
#include <iostream>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <vector>
#define CLOSE ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define isd(c) ('0' <= (c) && (c) <= '9')
#define isa(c) ('a' <= (c) && (c) <= 'z')
#define isA(c) ('A' <= (c) && (c) <= 'Z')
#define mem(a, b) memset(a, b, sizeof a);
#define N 100005
#define M 2000005
#define mod 1000000007
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define PI acos(-1)
#define endl "\n"
#define pii pair<int, int>
#define F first
#define S second
#define bug cout << endl << " .....here!...." << endl;
//#pragma GCC optimize("O3")
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
int main()
{
CLOSE
int t;
cin >> t;
// bbbbabbbba
while (t--)
{
int n, m, a, b, c;
cin >> n >> m >> a >> b >> c;
int maxn = 0;
for (int i = 0; i <= min(n, m / c); i++)
{
int sum = 0;
if (i == 0)
{
sum++;
sum += (n - 1) / a;
sum += (m - 1) / b;
}
else
{
sum++;
sum += (i - 1) * 2;
if (n - i >= 1)
{
sum++;
int x = n - i - 1;
sum += x / a;
}
if (c > b)
sum += (c - 1) / b * i;
if (m - c * i >= 1)
{
sum++;
int x = m - i * c - 1;
int md = (((c - 1) / b + 1) * b) - (c - 1);
if (x / md >= i)
sum += i, x -= md * i;
else
{
sum += x / md;
x -= x / md * md;
}
sum += x / b;
}
}
// cout << i << " " << sum << endl;
maxn = max(maxn, sum);
}
cout << maxn + 1 << endl;
}
return 0;
}
解题思路
T4
题面
奇怪的函数
时间限制:3秒 内存限制:256M
题目描述
小可有一个奇怪的函数 F(x), 这个函数的输入参数是一个正整数 x ,为了得到这个函数的运算结果,这个函数需要依次进行 n 个步骤,每个步骤是如下三种形式之一:
1.x+=vali
2.x=min(x,vali)
3.x=max(x,vali)
依次执行完这 n 个步骤之后,这个函数就可以安心输出答案了。
现在,小可得到了这个函数,他想简化这个函数,确切的来说,他有 q 个问题,每个问题要么是修改这个函数的某一个步骤,要么给定一个 x ,询问当前F(x) 的值,请帮助他完成这个过程。
输入描述
第一行:输入一个正整数 n ,表示这个函数的步骤数量。
接下来 n 行:每行输入两个正整数opt val
opt表示这是第几种操作,val表示这一次操作对应的权值。
接下来一行:输入一个正整数 q ,表示问题的个数。
接下来 q 行:每行输入一个问题。形式为如下的四个操作之一。
1 pos val
表示把第 pos 个步骤改成 x+=val
2 pos val
表示把第 pos 个步骤改成 x=min(x,val)
3 pos val
表示把第 pos 个步骤改成 x=max(x,val)
4 x
表示询问,此时 F(x) 是多少。
输出描述
对于每一个操作4的查询,输出一行一个数字表示答案。
输入样例
10
1 48
1 50
1 180
2 957
1 103
1 100
1 123
3 500
1 66
1 70
3
4 20
4 50
4 700
输出样例
760
790
1419
数据描述
对于100%的数据:1≤n,q≤3∗105,操作 2,3,4 涉及的 val 在[1,108][1,108] 之间,所有加法操作涉及的 val 在[1,200][1,200] 之间。
解题思路
5分部分
暴力枚举即可
7~11测试点部分
由于该函数再min断点之间是单调递增的,所以可以二分查找答案
code
真不巧,代码走丢了