A.构造
我们有着很简单的构造方法
对于所有的有 n − 1 n-1 n−1对匹配括号的序列
我们有两种方案将其变为 n n n对匹配括号的序列
- 在外层套一个 ( ) () ()
- 在最后添一个 ( ) () ()
可以知道,这两种方案是绝对不可能重复的
而我们又知道 n = 1 n=1 n=1时,有序列 ( ) () ()
因此,好办了
#include<bits/stdc++.h>
using namespace std;
vector<deque<char>> res[55];
int main()
{
res[1].push_back({'(',')'});
for (int i=2;i<=50;++i)
{
for (int j=0;j<res[i-1].size();++j)
{
deque<char> tmp = res[i-1][j];
tmp.push_back(')');
tmp.push_front('(');
res[i].push_back(tmp);
tmp.pop_back();
tmp.pop_front();
tmp.push_back('(');
tmp.push_back(')');
res[i].push_back(tmp);
if (res[i].size()>i)break;
}
}
int t;scanf("%d",&t);
while (t--)
{
int n;scanf("%d",&n);
for (int i=0;i<n;++i)
{
for (char ch:res[n][i])printf("%c",ch);
puts("");
}
}
}
’
B.思维
如果让我们直接去构造一个有着 m m m对满足条件的索引的序列,那确实是非常困难的
但是这题我们只需判断是否存在
通过画图,我们可以很简单地归纳出一个结论(猜测也是可以的)
序列中满足条件的索引数一定是连续变化的!!
即,如果能组成的序列中最小的满足条件数为 m i n min min,最大为 m a x max max
那么对于 m i n ≤ m ≤ m a x min\le m\le max min≤m≤max m m m一定能够被取到!
问题就为如何计算出 m i n min min和 m a x max max了
m a x max max很好求,颜色相同的放一起就好了 a + b + c − 3 a+b+c-3 a+b+c−3
m i n min min的求法为 M A X ( 0 , a − b − c − 1 ) MAX(0,a-b-c-1) MAX(0,a−b−c−1)
即我们每次都从最大的两种颜色中取出来放进去
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;scanf("%d",&t);
while (t--)
{
int a,b,c,m;
scanf("%d %d %d %d",&a,&b,&c,&m);
if (a<b)swap(a,b);
if (a<c)swap(a,c);
int mn = max(0,a-b-c-1);
int mx = a+b+c-3;
if (m<=mx&&m>=mn)printf("YES\n");
else printf("NO\n");
}
}
C.思维+推公式
并不是很难
按照题目的含义将公式列出:
假设我们选择了第 i i i个英雄去攻击,所有英雄的强度和为 s u m sum sum
那么我们需要的金币为:
m a x ( 0 , a t t a c k − a [ i ] ) + m a x ( d e f e n s e − s u m + a [ i ] ) max(0,attack-a[i])+max(defense-sum+a[i]) max(0,attack−a[i])+max(defense−sum+a[i])
我们敏锐的发现, a [ i ] a[i] a[i]似乎可以被消掉
即,如果存在 a [ i ] a[i] a[i],使得 a t t a c k − a [ i ] ≥ 0 attack-a[i]\ge 0 attack−a[i]≥0并且 d e f e c s e − s u m + a [ i ] ≥ 0 defecse-sum+a[i]\ge 0 defecse−sum+a[i]≥0
那么此刻的所需要的金币最小,为 a t t a c k + d e f r c s e − s u m attack+defrcse-sum attack+defrcse−sum
因此,我们去找是否有 s u m − d e f e c s e ≤ a [ i ] ≤ a t t a c k sum-defecse\le a[i]\le attack sum−defecse≤a[i]≤attack
如果没有的话。则代入该区间两边最靠近的值,取小即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 3e18;
const int maxn = 2e5+100;
ll a[maxn];
int n,m;
ll at,df;
ll sum;
inline ll cal(int id)
{
return max(0LL,at-a[id])+max(0LL,df-sum+a[id]);
}
int main()
{
scanf("%d",&n);
sum=0;
for (int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
sum+=a[i];
}
sort(a+1,a+1+n);
scanf("%d",&m);
while (m--)
{
scanf("%lld %lld",&at,&df);
ll ans = inf;
int id = lower_bound(a+1,a+1+n,at)-a;
if (id<=n&&id>=1)ans = min(ans,cal(id));
if (id>1)ans = min(ans,cal(id-1));
id = lower_bound(a+1,a+1+n,sum-df)-a;
if (id<=n&&id>=1)ans = min(ans,cal(id));
if (id>1)ans = min(ans,cal(id-1));
printf("%lld\n",ans);
}
}
D.搜索剪枝
很明显的一个思路:
我们只需要搜索出前 m + 1 m+1 m+1大的组合,就一定能够找到答案!
好的,大致方针已经定了下来
那么如何搜索呢?
首先,最大的一定是 [ c 1 , c 2 , c 3 , … , c n ] [c_1,c_2,c_3,\dots,c_n] [c1,c2,c3,…,cn]
然后,依次我们有搜索方向 [ c 1 − 1 , c 2 , … , c n ] , [ c 1 , c 2 − 1 , … , c n ] , … , [ c 1 , c 2 , … , c n − 1 ] [c_1-1,c_2,\dots,c_n],[c_1,c_2-1,\dots,c_n],\dots,[c_1,c_2,\dots,c_n-1] [c1−1,c2,…,cn],[c1,c2−1,…,cn],…,[c1,c2,…,cn−1]
依靠这种搜索,我们由大到小绝对不会遗漏!
但是关键就是搜索的状态太多了!
考虑到我们只需要搜出前 m + 1 m+1 m+1个即可!
我们可以消减状态数
具体操作
维护一个 s e t set set由大到小,装入每种组合
每次我们拿出最大的组合,然后遍历其所有的搜索方向得到新的组合插入 s e t set set中
接下来,如果 s e t set set的大小超过 m + 1 m+1 m+1我们就不断地减去 s e t set set中最小的组合
如此,我们可以完全遍历出前 m + 1 m+1 m+1大的组合
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
set<array<int,10>> se;
int G[15][maxn];
int len[15];
int n,m;
struct node
{
array<int,10> ar;
int sum = 0;
void init()
{
for (int i=0;i<10;++i)ar[i]=0;
sum=0;
}
bool operator<(const node& nod)const
{
if (sum==nod.sum)return ar>nod.ar;
return sum>nod.sum;
}
};
set<node> que;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
{
scanf("%d",&len[i]);
for (int j=1;j<=len[i];++j)scanf("%d",&G[i][j]);
}
scanf("%d",&m);
for (int i=1;i<=m;++i)
{
array<int,10> ar;
for (int j=0;j<10;++j)ar[j]=0;
for (int j=0;j<n;++j)scanf("%d",&ar[j]);
se.insert(ar);
}
node cur;
cur.init();
for (int i=0;i<n;++i)
{
cur.ar[i]=len[i+1];
cur.sum+=G[i+1][len[i+1]];
}
que.insert(cur);
while (!que.empty())
{
cur = *que.begin();
que.erase(que.begin());
if (!se.count(cur.ar))
{
for (int i=0;i<n;++i)printf("%d ",cur.ar[i]);
return 0;
}
for (int i=0;i<n;++i)if (cur.ar[i]>1)
{
node net;net.sum=0;
for (int j=0;j<10;++j)net.ar[j]=cur.ar[j];
net.ar[i]--;
for (int j=0;j<10;++j)net.sum+=G[j+1][net.ar[j]];
que.insert(net);
while (que.size()>m+1)que.erase(--que.end());
}
}
}