算法分享:
这是一个交互问题。
有一个秘密序列
你需要找到任意两个索引
为此,您可以提出查询。每个查询的形式如下:选取任意索引
请找出任意两个索引
互动
每个测试用例的第一行都包含一个整数
要进行查询,您需要选择四个索引
打印以下格式的行:
然后,您将收到
您最多可以进行
接下来,如果你的程序找到了一对索引
请注意,这一行不被视为查询,在计算查询次数时也不被考虑在内。
之后,进入下一个测试用例。
如果您在交互过程中提出的查询次数超过
打印查询或测试用例的答案后,不要忘记输出行尾并刷新输出。否则,您将得到
保证所有测试用例中
排列组合的定义是什么?
什么情况下
或运算的特点是什么?
或运算的基本定律有什么?
设序列最大值为
显然,或运算中
这意味着,第一轮我们执行
显然,
问题是,
通过上述内容发现,我们可以执行$a_i | p_{max} ? a_k | p_{max}
异或线性方程
最后一步,遍历解集范围,找到任意一个
综上所述,在最坏的
#include <bits/stdc++.h>
using namespace std;
int question1(int a, int b)
{
cout << "? " << a << " " << a << " " << b << " " << b << endl;
char c;
cin >> c;
if (c == '<')
return b;
return a;
}
char question2(int a, int b, int location)
{
cout << "? " << a << " " << location << " " << location << " " << b << endl;
char c;
cin >> c;
return c;
}
int question3(int a, int b)
{
cout << "? " << a << " " << a << " " << b << " " << b << endl;
char c;
cin >> c;
if (c == '<')
return a;
return b;
}
void solve()
{
int n;
cin >> n;
int location = -1;
cout << "? 0 0 1 1" << endl;
char c;
cin >> c;
if (c == '<')
location = 1;
if (c == '>')
location = 0;
for (int i = 2; i <= n - 1; i++)
{
location = question1(location, i);
}
// 已询问n-1次
int t = 0, maxxor = 0;
int x = n - 1;
while (x)
{
t++;
x >>= 1;
}
for (int i = 0; i < t; i++)
maxxor += (1 << i);
// finalmax
// cout << location << " " << maxxor << endl;
vector<int> ans;
ans.push_back(0);
for (int i = 1; i <= n - 1; i++)
{
char c = question2(ans[0], i, location);
if (c == '=')
{
ans.push_back(i);
}
else if (c == '<')
{
ans.clear();
ans.push_back(i);
}
else
continue;
}
// 已询问2n-2次
int pos = -1;
for (auto &p : ans)
{
if (pos == -1)
pos = p;
else
{
pos = question3(pos, p);
}
}
cout << "! " << pos << " " << location << endl;
return;
}
int main()
{
int t;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
给定由
其中,
这里的
怎样差异化最大?和最小值与最大值有没有关系?
排序之后
想快点。。。
你有
人民币曾经发行的面值是
能不能直接贪心?为什么?如果加一个
建议看官网的贪心正确性证明,这里只讨论取倍数的问题。显然单位元为最小公倍数
显然,需要计算取多少次
结果为
这是一个互动问题。
给你一个行数为
在每次查询中,您可以选择任意网格单元
您的任务是在进行查询后确定其中一个地雷的位置。
互动
对于每个测试用例,交互从阅读
然后,您最多可以按以下方式进行
"? x y" (
每次查询后,您都应该读取一个等于
找到任何一个地雷的位置后,打印一行"!x y"(不带引号),表示其中一个地雷的行和列。输出答案不算查询。
打印答案后,程序必须继续解决剩余的测试用例,如果所有测试用例都已解决,则退出程序。
这个问题的交互器不是自适应的:在进行任何查询之前,地雷的单元格都是固定的。
打印查询后,不要忘记输出行尾并刷新输出。否则会出现 "超过闲置限制 "的提示。为此,请使用
根据样例不难想到肯定要问一个角
询问该直线和边的两个交点(注意坐标计算的问题),又得到了什么信息?
什么条件下,这里已经可以终止询问了?
询问
为什么说问三次就行,因为如果
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> p;
int d1, d2, d3, d4;
int n, m;
p l, r;
void readfirst()
{
cout << "? " << 1 << " " << 1 << endl;
cin >> d1;
l = {d1 + 1, 1};
r = {1, d1 + 1};
if (l.first > n)
{
l.first = n;
l.second = d1 + 2 - n;
}
if (r.second > m)
{
r.second = m;
r.first = d1 + 2 - m;
}
cout << "? " << l.first << " " << l.second << endl;
cin >> d2;
cout << "? " << r.first << " " << r.second << endl;
cin >> d3;
return;
}
void solve()
{
cin >> n >> m;
int maxp = n + m - 2;
readfirst();
if (d2 & 1)
{
int y = (d1 + 2 + r.second - r.first - d3) / 2;
int x = d1 + 2 - y;
cout << "! " << x << " " << y << endl;
// return;
}
else if (d3 & 1)
{
int y = (d1 + 2 + d2 + l.second - l.first) / 2;
int x = d1 + 2 - y;
cout << "! " << x << " " << y << endl;
}
else
{
int y1 = (d1 + 2 + r.second - r.first - d3) / 2;
int x1 = d1 + 2 - y1;
int y2 = (d1 + 2 + d2 + l.second - l.first) / 2;
int x2 = d1 + 2 - y2;
cout << "? " << x1 << " " << y1 << endl;
int d4;
cin >> d4;
if (d4 == 0)
{
cout << "! " << x1 << " " << y1 << endl;
}
else
cout << "! " << x2 << " " << y2 << endl;
}
}
int main()
{
int t;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
给定一个初始值为
判断是否可以使用最多
不需要尽量减少操作次数。
这里的
数据范围达到
tips:他们都是特殊的二进制数。
除最高位之外,
要随时保证
首先目标值
其次,样例不难得出
仅讨论
本体讨论时从最高位向最低位讨论,位指针
若
若
先考虑
这时如果
如果第一个不同的位
此时若第一步将
所以第一个不同的位
相反,若第一个不同的位
因此,后面的错分设位,让
综上所述,
还有就是,题没有问你要选择那个数来进行异或,只要你给出操作过后
其实问的话也不费事而已,就多了一步。要先求选择异或的数再计算操作后
#include <bits/stdc++.h>
using namespace std;
#define int long long
map<int, int> power;
int get_highest_one(int x)
{
for (int i = 62; i >= 0; i--)
{
if ((x >> i) & 1)
return 1;
}
return -1;
}
void init()
{
for (int j = 0; j <= 62; j++)
{
power[(1LL << j)] = 1;
}
}
void solve()
{
int n, m;
cin >> n >> m;
int t = 0;
while ((1LL << t) <= n)
{
t++;
}
if (power[n])
{
cout << -1 << endl;
return;
}
if ((n ^ m) < n)
{
cout << 1 << endl;
cout << n << " " << m << endl;
return;
}
for (int i = t - 2; i >= 0; i--)
{
if (((n >> i) & 1) != ((m >> i) & 1))
{
if (((m >> i) & 1))
{
cout << -1 << endl;
return;
}
else
{
int ans = 0;
for (int j = 0; j <= i; j++)
{
ans += (1LL << j);
}
cout << 2 << endl;
cout << n << " " << ans << " " << m << endl;
}
return;
}
}
return;
}
signed main()
{
init();
int t;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
在硕士援助中心,Nyam-Nyam 接受了一项信息学方面的家庭作业。
有一个长度为
请帮助 Nyam-Nyam 找到合适的分块方法,或者确定不存在合适的解。
注释:
数组中的
例如
如果存在可能的分法,每一段的
分两组就够了。如何判断能分成两组?
对于问题而言,如果有解得话,全局的
开桶爆搜全局得到
如果这一组直接分完了才是
否则,若剩下的一组查询完
否则是可能的一种分法
写法上,可以分两段爆搜出两组最小
#include <bits/stdc++.h>
using namespace std;
int a[200001];
void solve()
{
map<int, bool> mp;
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
mp[a[i]] = 1;
}
int mex = 0;
for (int i = 0; i < n; i++)
{
if (mp[i] == 0)
{
mex = i;
break;
}
}
int l = 1;
map<int, bool> mp1, mp2;
bool tag1 = 0;
for (l; l <= n; l++)
{
mp1[a[l]] = 1;
if (mp1.size() >= mex)
for (int i = 0; i <= mex; i++)
{
if (i == mex)
{
if (mp1[i] == 0)
tag1 = 1;
}
else if (mp1[i] == 0)
{
break;
}
}
if (tag1)
break;
}
if (l >= n)
{
cout << -1 << endl;
return;
}
for (int i = l + 1; i <= n; i++)
mp2[a[i]] = 1;
for (int i = 0; i <= mex; i++)
{
if (i == mex)
{
if (mp2[i] == 0)
{
cout << 2 << endl;
cout << 1 << " " << l << endl;
cout << l + 1 << " " << n << endl;
}
else
cout << -1 << endl;
}
else if (mp2[i] == 0)
{
cout << -1 << endl;
return;
}
}
return;
}
int main()
{
int t;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
凯夫特梅鲁姆硕士生援助中心的短信平台计划进行更新,开发人员希望在更新中优化显示给用户的信息集。目前共有
请注意,读取1个编号为
用户可以确定他愿意在信使中花费的时间
短信的开发人员未能实现这一功能,因此他们要求您解决这一问题。
输入
每个测试由多个测试用例组成。第一行包含一个整数
每个测试用例的第一行包含两个整数
接下来
保证所有测试用例的
随机抓一组数据的情况下,如何让函数的结果最小?
对于
优先塞
对于随机抓的一组数据,必然是
那么
以
选择后的最优结果为
首先将
(注意,因为基底的要求,
这个时候有个问题,后面还有
解决办法就是本周的关键算法——反悔贪心。
在付出一定的
反悔谁呢?
反悔占地方最大的那个!
所以使用大根堆维护已选的
如果反悔一次后可以塞入就塞入,反悔了后还是塞不进去就放弃这个(因为牺牲
双层暴力加堆操作,时间复杂度
以
设
则有
维度优化:暴力时就记忆
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 20001;
pair<int, int> p[maxn]; //{b,a}
int id[maxn];
void solve()
{
int n, l;
cin >> n >> l;
for (int i = 1; i <= n; i++)
{
cin >> p[i].second >> p[i].first;
}
sort(p + 1, p + 1 + n);
int size_final = 0, now = 0;
p[n + 1] = {0, 0};
id[n + 1] = n + 1;
for (int i = n; i >= 1; i--)
{
if (p[i].first == p[i + 1].first)
id[i] = id[i + 1];
else
id[i] = i + 1;
}
for (int i = 1; i <= n; i++)
{
int nowb = p[i].first;
int nowa = p[i].second;
if (nowa > l)
continue;
priority_queue<int> q;
int ans = nowa;
for (int j = i + 1; j < id[i]; j++)
{
if (ans + p[j].second <= l)
{
ans += p[j].second;
q.push(p[j].second);
}
else
break;
}
int size = q.size() + 1;
for (int j = id[i]; j <= n; j++)
{
if (nowb != p[j].first)
{
ans += p[j].first - nowb;
nowb = p[j].first;
}
while (!q.empty() && ans > l)
{
ans -= q.top();
q.pop();
}
if (ans + p[j].second <= l)
{
ans += p[j].second;
q.push(p[j].second);
}
else
{
if (!q.empty())
{
if (p[j].second < q.top())
{
ans -= q.top();
q.pop();
ans += p[j].second;
q.push(p[j].second);
}
}
}
size = max(size, (int)q.size() + 1);
}
size_final = max(size_final, size);
}
cout << size_final << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define int long long
using p = pair<int, int>;
const int maxn = 5001;
const int INF = numeric_limits<int>::max();
const int inf = numeric_limits<int>::min();
int n, l;
int a[maxn], b[maxn];
p pd[maxn];
int dp[maxn][maxn];
void solve()
{
cin >> n >> l;
for (int i = 1; i <= n; i++)
{
cin >> pd[i].second >> pd[i].first;
}
sort(pd + 1, pd + 1 + n);
for (int i = 1; i <= n; i++)
{
a[i] = pd[i].second;
b[i] = pd[i].first;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
dp[1][i] = a[i];
ans = max(dp[1][i] <= l ? 1LL : 0LL, ans);
}
for (int i = 2; i <= n; i++)
{
int mintime = INF;
for (int j = i; j <= n; j++)
{
mintime = min(dp[i - 1][j - 1] - b[j - 1], mintime);
dp[i][j] = mintime + b[j] + a[j];
ans = max(dp[i][j] <= l ? i : inf, ans);
}
}
cout << ans << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
硕士生援助中心公布了入学考试,考试内容如下。
给考生一个大小为
你的朋友想进入中心工作。请帮助他通过考试!
输入
每个测试由多个测试用例组成。第一行包含一个整数
每个测试用例的第一行包含两个整数
每个测试用例的第二行包含
保证所有测试用例中
容斥原理。
容斥原理,单独算就完了。
容斥部分
如果去掉条件保证
#pragma GCC Optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 300001;
int a[maxn];
void solve()
{
int n, c;
cin >> n >> c;
int even = 0, odd = 0;
int ans1 = 0, ans2 = 0, ans3 = 0, ans4 = 0;
vector<int> ji;
vector<int> ou;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (a[i] & 1)
{
ji.push_back(a[i]);
}
else
ou.push_back(a[i]);
int f1 = c - a[i] + 1;
if (f1 >= 0)
ans1 += f1;
if (a[i] <= c)
ans2 += (a[i] / 2 + 1);
else if (a[i] > c && a[i] <= 2 * c)
{
ans2 += (c - a[i] / 2 + 1);
}
}
int size1 = ji.size(), size2 = ou.size();
set<pair<int, int>> s;
for (int i = 0; i < size1; i++)
{
if (ji[i] > c)
break;
int p = upper_bound(ji.begin(), ji.end(), 2 * c - ji[i]) - ji.begin();
ans3 += p - i;
}
for (int j = 0; j < size2; j++)
{
if (ou[j] > c)
break;
int p = upper_bound(ou.begin(), ou.end(), 2 * c - ou[j]) - ou.begin();
ans4 += p - j;
}
cout << (c + 1) * (c + 2) / 2 - (ans1 + ans2 - (ans3 + ans4)) << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
最初有一个空字符串
袋
对
给定字符串
如果无法使最后的
限制因素
爆搜可不可行?最坏的时间复杂度是多少?
像不像背包?
按层
设
则有转移方程
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
// #define int long long
const int mod = 998244353;
const int maxn = 201;
using p = pair<int, int>;
string T;
int n;
vector<int> sizer(101);
vector<string> a[1001];
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr)
int dp[maxn][maxn];
int main()
{
IOS;
cin >> T;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> sizer[i];
// a[i].push_back("");
for (int j = 0; j < sizer[i]; j++)
{
string so;
cin >> so;
a[i].push_back(so);
}
}
string f = T;
memset(dp, mod, sizeof(dp));
for (int i = 0; i <= maxn; i++)
{
dp[i][0] = 0;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= f.size(); j++)
{
dp[i][j] = dp[i - 1][j];
for (int k = 0; k < a[i - 1].size(); k++)
{
int length = a[i - 1][k].size();
if (length > j)
continue;
bool tag = 1;
for (int p = j - length, q = 0; p < j; p++, q++)
{
if (f[p] != a[i - 1][k][q])
{
tag = 0;
break;
}
}
if (tag)
dp[i][j] = min(dp[i][j], dp[i - 1][j - length] + 1);
}
}
}
if (dp[n][f.size()] <= n)
{
cout << dp[n][f.size()] << endl;
}
else
cout << -1 << endl;
system("pause");
return 0;
}
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
// #define int long long
const int mod = 998244353;
const int maxn = 201;
using p = pair<int, int>;
string T;
int n;
vector<int> sizer(101);
vector<string> a[1001];
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr)
int dp[maxn];
int g[maxn];
int main()
{
IOS;
cin >> T;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> sizer[i];
// a[i].push_back("");
for (int j = 0; j < sizer[i]; j++)
{
string so;
cin >> so;
a[i].push_back(so);
}
}
string f = T;
memset(dp, mod, sizeof(dp));
dp[0] = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= f.size(); j++)
{
g[j] = dp[j];
for (int k = 0; k < a[i - 1].size(); k++)
{
int length = a[i - 1][k].size();
if (length > j)
continue;
bool tag = 1;
for (int p = j - length, q = 0; p < j; p++, q++)
{
if (f[p] != a[i - 1][k][q])
{
tag = 0;
break;
}
}
if (tag)
g[j] = min(g[j], dp[j - length] + 1);
}
}
swap(dp,g);
}
if (dp[f.size()] <= n)
{
cout << dp[f.size()] << endl;
}
else
cout << -1 << endl;
system("pause");
return 0;
}
给你一个长度为
请按照给出的顺序处理
1 x y
:在 2 x
:从 保证在处理完每一个查询后,
处理完所有查询后,打印
查询强度
本题为
这意味着,只要插入链表时用
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int INF=1e18;
using p=pair<int,int>;
#define endl "\n"
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
#undef int
int main()
{
#define int long long
IOS;
list<int>l;
map<int,list<int>::iterator>mp;
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
int a;
cin>>a;
l.push_back(a);
mp[a]=--l.end();
}
int q;
cin>>q;
for(int j=1;j<=q;j++)
{
int t;
cin>>t;
if(t==1)
{
int x,y;
cin>>x>>y;
auto it=mp[x];
it++;
l.emplace(it,y);
mp[y]=--it;
}
else
{
int x;
cin>>x;
l.erase(mp[x]);
}
}
for(auto p:l)
{
cout<<p<<" ";
}
cout<<endl;
system("pause");
return 0;
}
给定长度为
要求选择了
数据保证
去掉要求如何贪心?
什么时候出现反悔行为?满足什么条件才能反悔?反悔时的操作是什么?
反悔时肯定是选中间的没有选择两边的思想更优,所以在取一个点的时候将它左右两边的点与中间作差的结果记入该点并更新其左右邻居信息为反悔备选点的左右邻居端点进行维护,此时就可以把反悔点视作新的结点操作了。
来自
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 100;
struct node{
int id,w;
node(){
id=w=0;
}
node(int x, int y){
id=x,w=y;
}
bool operator<(const node &x) const{
return w<x.w;
}
};
int n,k,a[N],l[N],r[N];
bool vis[N];
priority_queue<node> q;
typedef long long ll;
ll ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
l[i]=i-1,r[i]=i+1;
q.push(node(i,a[i]));
}
for(int i=1;i<=k;i++){
while(vis[q.top().id])q.pop();
node now=q.top();
if(now.w<0)break;
q.pop();
ans+=now.w;
a[now.id]=now.w=a[l[now.id]]+a[r[now.id]]-a[now.id];
q.push(now);
vis[l[now.id]]=vis[r[now.id]]=1;
l[now.id]=l[l[now.id]],r[now.id]=r[r[now.id]];
l[r[now.id]]=r[l[now.id]]=now.id;
}
cout<<ans<<'\n';
}
请我喝杯咖啡吧~