开局天崩构造题,确实不太会构造题。我的做法是黑白砖块交替出现(黑先),判断最后黑的砖块是比白的多1还是相等,若相等则将最后一行的一个白砖变为黑砖
#include
#define ll long long
#define ull unsigned long long
#define pb push_back
#define lowbit(x) ((x)&(-x))
#define inf 0x3f3f3f3f
#define endl "\n"
#define MP(x,y) (make_pair(x,y))
#define pll pair
#define pi 3.1415926535
using namespace std;
char s[105][105];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
bool f=1;
scanf("%d%d",&n,&m);
int num = 0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(f) s[i][j]='B',num++;
else s[i][j]='W',num--;
f=!f;
}
s[i][m]=0;
}
if(num==0)
for(int i=0;i<m;i++){
if(s[n-1][i]=='W'){
s[n-1][i]='B';
break;
}
}
for(int i=0;i<n;i++){
cout<<s[i]<<endl;
}
}
}
注意到题目的条件只能用前面的元素更新后面,故倒过来做看前面是否存在能完成操作的元素
#include
#define ll long long
#define ull unsigned long long
#define pb push_back
#define lowbit(x) ((x)&(-x))
#define inf 0x3f3f3f3f
#define endl "\n"
#define MP(x,y) (make_pair(x,y))
#define pll pair
#define pi 3.1415926535
using namespace std;
int n,a[100005],b[100005];
map<int,int> num;
int main(){
int t;
scanf("%d",&t);
while(t--){
num[0]=0,num[1]=0,num[-1]=0;
scanf("%d",&n);
bool f= 1;
for(int i=0;i<n;i++) scanf("%d",&a[i]),num[a[i]]++;
for(int i=0;i<n;i++) scanf("%d",&b[i]);
for(int i=n-1;i>=0;i--){
num[a[i]]--;
if(b[i]>a[i] && num[1]==0){
f=0;
break;
}
if(b[i]<a[i] && num[-1]==0){
f=0;
break;
}
}
puts(f?"YES":"NO");
}
}
计数题,维护下前缀和,显然前缀和相等的两个位置中间段的和为0,存下每一个元素之前的第一个与他前缀和相等的位置。枚举左端点,找不包含0区间的最大右端点。可以用个set,或者单调栈。
#include
#define ll long long
#define ull unsigned long long
#define pb push_back
#define lowbit(x) ((x)&(-x))
#define inf 0x3f3f3f3f
#define endl "\n"
#define MP(x,y) (make_pair(x,y))
#define pll pair
#define pi 3.1415926535
using namespace std;
int n,a[200005];
ll sum[200005];
map<ll,int> ver;
struct nd{
int l,r;
}p[200005];
bool cmp(nd x,nd y){
return x.l<y.l;
}
multiset<int> s;
int tot;
int main(){
scanf("%d",&n);
ll ans = 0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
if(ver[sum[i]]){
p[tot].l = ver[sum[i]]+1;
p[tot].r = i;
tot++;
s.insert(i);
}else if(sum[i]==0){
p[tot].l = 1;
p[tot].r = i;
tot++;
s.insert(i);
}
ver[sum[i]] = i;
}
sort(p,p+tot,cmp);
s.insert(n+1);
int now = 0;
for(int i=1;i<=n;i++){
while(now<tot && p[now].l<i){
//cout<
s.erase(s.find(p[now].r));
now++;
}
ans +=(*s.begin()-i);
}
cout<<ans<<endl;
}
暴力题,先将每一轮最多的操作做完,可以得到最小的轮次数和操作数,操作数即最大的轮次数,加上k在这个区间之外就输出-1,否则用这些操作构造k轮即可。
#include
#define ll long long
#define ull unsigned long long
#define pb push_back
#define lowbit(x) ((x)&(-x))
#define inf 0x3f3f3f3f
#define endl "\n"
#define MP(x,y) (make_pair(x,y))
#define pll pair
#define pi 3.1415926535
using namespace std;
int n,k;
char s[3005];
vector<int> ans[3000005];
int p[3000005];
int main(){
scanf("%d%d",&n,&k);
scanf("%s",s+1);
int num = 0;
int sum = 0;
while(1){
for(int i=1;i<=n;i++){
if(s[i]=='R' && s[i+1] == 'L'){
s[i]='L',s[i+1]='R';
ans[num].pb(i),i++;
sum++;
}
}
if(ans[num].size()==0)
break;
num++;
}
if(sum<k || num>k){
puts("-1");
}else{
int x = num;
for(int i=0;i<num;i++){
while(ans[i].size()-p[i]>1 && x<k){
x++;
printf("1 %d\n",ans[i][p[i]]);
p[i]++;
}
printf("%d",ans[i].size()-p[i]);
for(int j=p[i];j<ans[i].size();j++){
printf(" %d",ans[i][j]);
}
puts("");
}
}
return 0;
}
看傻了,不会构造,再见
当时把题读反了,吐血。考虑构造一个大小为k的子集,要是任意两个元素的最大gcd最小,显然优先把1和质数放入集合,把1和这些质数当做基,假设你现在拥有的集合的gcd为m-1,当你加入下一个数后gcd变为m,你可以加入小于等于m的最小质因子的基(1,2,3,5…)*m。然后我们可以这样考虑,当我们加入一个数a后,这个集合的gcd变为了a/(a的最小质因子),故我们将2~n的每个数除以它的最小质因子排序即可。
#include
#define ll long long
#define ull unsigned long long
#define pb push_back
#define lowbit(x) ((x)&(-x))
#define inf 0x3f3f3f3f
#define endl "\n"
#define MP(x,y) (make_pair(x,y))
#define pll pair
#define pi 3.1415926535
using namespace std;
int n;
vector<int> a;
bool v[500005];
int main(){
scanf("%d",&n);
for(int i=2;i<=n;i++){
if(v[i]) continue;
for(int j=i;j<=n;j+=i){
if(v[j]) continue;
a.pb(j/i);
v[j]=1;
}
}
sort(a.begin(),a.end());
for(int i=0;i<a.size();i++){
cout<<a[i]<<' ';
}
}