离散化+区间覆盖
离散化其实就是把很大的数据范围映射到它们在数组中对应的位置
但是这道题比较特别,因为在输入的左右值不是海报区间的左右端点而是左右的块,导致在覆盖的时候如果一张海报(c)的最前面的单元块的编号比在它前面的某个海报(b)的最后一个单元块多2,(网上都说是大于1但是其实直接等于2可以省掉好多空间)
我们在映射的时候获得的新数组是[1,2],[2,3],[3,4]对应原来的[1,4],[4,6],[6,9],就会导致这两张海报把它们前面的卡在b开头c结尾的海报( a )覆盖。所以就不要那么拮据,如果两张海报之间相差2,我们直接在它们中间再插入一个数,让它卡着bc中间的那个位置,相当于在bc之间挤出一条缝让a不会被覆盖,这个挤出来的位置可以在后面lower_bound()排序找位置的时候起作用。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
/*调了3个小时 首先是符号错误 一个等于号写成加号 一直死循环 以后在最可能出错的地方
* 随便弄个cout<<"sb"<<endl;(如果实在不知道 真的可以在程序里二分查找bug 比较确定的话直接在最可能出错的函数里面输出 或者是可以养成
* 在打题的时候就先输出最不确定的值 (下次试试看)
* */
const int N=(10000<<1)+7;
struct node{
int l ,r,val;
}tr[N<<4];//在不超过范围内尽量开大吧
int vis[N],L[N],R[N],a[N<<2],ans;
void build(int k,int l,int r){
tr[k].l=l,tr[k].r=r;
if(l==r)return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void push_down(int k){
if(tr[k].val){
tr[k<<1].val=tr[k].val;tr[k<<1|1].val=tr[k].val;
tr[k].val=0;//下传之后应该置零 不然会在query的时候被pushdown导致更早的海报覆盖后来的海报
}
}
void update(int k,int l,int r,int num){
if(tr[k].l>=l&&tr[k].r<=r){
tr[k].val=num;
return;
}if(tr[k].val)push_down(k);
if(tr[k<<1].r>=l)update(k<<1,l,r,num);
if(tr[k<<1|1].l<=r)update(k<<1|1,l,r,num);
}
void query(int k,int l,int r){
if(tr[k].val&&!vis[tr[k].val]){
vis[tr[k].val]=1;
ans++;
push_down(k);
return;
} if(l==r){
return ;
}
if(tr[k].val)push_down(k);
int mid=(l+r)>>1;
query(k<<1,l,mid);
query(k<<1|1,mid+1,r);
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(tr,0,sizeof tr);
memset(vis,0,sizeof vis);
ans=0;
int tol=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&L[i],&R[i]);
a[++tol]=L[i];
a[++tol]=R[i];
}
sort(a+1,a+1+tol);
tol=unique(a+1,a+1+tol)-(a+1);//排序后才能用unique
int tmp=tol;//如果直接在循环里面 会增加很多数 甚至死循环 !!循环的条件的“常数”不许更改
for(int i=2;i<=tol;i++){
if(a[i]-a[i-1]==2)a[++tmp]=a[i-1]+1;//这个题很重点就是给的数值不是端点而是“线段”,如果是点,夹在两个相差为2的数,原来的值就会直接被废弃,但是“线段”情况下之前的值仍然被保存
}
tol=tmp;
sort(a+1,a+1+tol);
build(1,1,tol);
for(int i=1;i<=n;i++){
int ll=lower_bound(a+1,a+1+tol,L[i])-(a);
int rr=lower_bound(a+1,a+1+tol,R[i])-(a);
update(1,ll,rr,i);
}
query(1,1,tol);
printf("%d\n",ans);
}
return 0;
}
#include<cstdio>
#define ll long long
const int N=100007;
struct node{
int l,r;
ll sum,lz;
}tr[N<<2];
ll a[N];
void push_up(int k){tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;}
void build(int k,int l,int r){
tr[k].l=l,tr[k].r=r;
if(l==r){
tr[k].sum=a[l];
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
push_up(k);
}
void push_down(int k){
if(!tr[k].lz)return ;
tr[k<<1].lz+=tr[k].lz;
tr[k<<1|1].lz+=tr[k].lz;
tr[k<<1].sum+=(tr[k<<1].r-tr[k<<1].l+1)*tr[k].lz;
tr[k<<1|1].sum+=(tr[k<<1|1].r-tr[k<<1|1].l+1)*tr[k].lz;
tr[k].lz=0;
}
ll query(int k,int l,int r){
if(tr[k].l>=l&&tr[k].r<=r){
return tr[k].sum;
}
push_down(k);
ll ans=0;
if(tr[k<<1].r>=l)ans+=query(k<<1,l,r);
if(tr[k<<1|1].l<=r)ans+=query(k<<1|1,l,r);
return ans;
}
void modify(int k,int l,int r,ll num){
if(tr[k].l>=l&&tr[k].r<=r){
tr[k].sum+=num*(tr[k].r-tr[k].l+1);
tr[k].lz+=num;
return;
}
push_down(k);//每一个“向下”的操作都需要push_down
if(tr[k<<1].r>=l)modify(k<<1,l,r,num);
if(tr[k<<1|1].l<=r)modify(k<<1|1,l,r,num);
push_up(k);
return ;
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
build(1,1,n);
for(int i=1;i<=q;i++){
char op;
int l,r;
scanf("\n%c%d%d",&op,&l,&r);
if(op=='Q')printf("%lld\n",query(1,l,r));
else if(op=='C'){
ll num;
scanf("%lld",&num);
modify(1,l,r,num);
}
}
}
/*
* a 10my 只想到应该是很直接地在某个地方插入就好 但是无法推出哪个位置
如果全部是a必然不行,否则选择插在开头或者插在结尾就好,如果不是全部都是a,其实如果a插在前面是回文,那么插在后面必然不是回文
简单模拟一下就好
其实真的可以大胆猜测最后答案的形式,一般规律都比较简单 几个例子都满足就先打 打了之后再去验证正确性
a_______a
_______aa
aa_____aa
aa____aaa
aaa___aaa
aaa__aaaa
aaaa_aaaa
aaaaaaaaa*/
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
while(n--)
{
string s;
cin>>s;
int flag=1;
for(int i=0;i<s.length();i++)
{
if(s[i]!='a'){
flag=0;
break;
}
}
if(flag)cout<<"NO"<<endl;
else {
string a=s+'a',b='a'+s;
for(int i=0;i<a.length();i++)
{
if(a[i]!=a[a.length()-1-i])
{
cout<<"YES"<<endl<<a<<endl;
break;
}
if(b[i]!=b[b.length()-1-i])
{
cout<<"YES"<<endl<<b<<endl;
break;
}
}
}
}
}
/* b
* 100my 前缀和的思想 把全部的操作都集中在一起完成就可以了 而且很有对称性
* 用一个数字就可以表示当前的状态 而不必真的把它们全都进行翻转 用数字表示状态 用空间换操作时间的一种体现
*/
#include <iostream>
#include<cstring>
using namespace std;
int v,cnt[300005];
int main()
{
int t;
cin>>t;
while(t--)
{
v=0;
memset(cnt,0,sizeof(cnt));///绝了!不可再被坑,多样例必须初始化
int n;
cin>>n;
string a,b;
cin>>a>>b;
if(a[0]=='1')cnt[0]=1;
for(int i=1;i<a.length();i++)
{
if(a[i]=='1')cnt[i]=cnt[i-1]+1;
else cnt[i]=cnt[i-1];
}
if(n%2&&(a[n-1]!=b[n-1]))
{
cout<<"NO"<<endl;
continue;
}
else
{
int pos=n-1,fl=1;
if(pos==0){
cout<<"YES"<<endl;
continue;
}
else {
if(n%2)pos--;
while(pos!=-1){
if((a[pos]==b[pos]&&a[pos-1]==b[pos-1]&&v%2==0)||
((a[pos]!=b[pos]&&a[pos-1]!=b[pos-1]&&v%2)));
else if(((a[pos]==b[pos]&&a[pos-1]==b[pos-1]&&v%2)||
(a[pos]!=b[pos]&&a[pos-1]!=b[pos-1]&&v%2==0))&&cnt[pos]*2==pos+1){
v++;
}
else {
cout<<"NO"<<endl;
fl=0;
break;
}
pos-=2;
}
}
if(fl)cout<<"YES"<<endl;
}
}
return 0;
}
/* c 30myself
* 其实和第一题有点像,需要观察一下很明显的不可行的情况 前后是0,0个数是奇数,总个数奇数
* 然后又把内容进行分割找出相同的地方 其实也算是题目的明显提示的 “1”表示的相同情况
* 分类讨论,之所以对于0不可以采用和1一样的贪心策略先在前面尽量加入足够多的“(”, 是因为可能很多0在前面出现导致这种贪心会让第二排完全走向贪心的
* 反面,也就是最差的方面,所以就找最”中庸“对两方都对等的方法。交替进行(对称性体现得太美了!)
*/
#include<iostream>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n,cnt0=0;
cin>>n;
string s,a,b;
cin>>s;
if(n%2||s[n-1]=='0'||s[0]=='0'){
cout<<"NO"<<endl;
continue;
}
for(int i=0;i<s.length();i++){
if(s[i]=='0')cnt0++;
}
int cnt1=n-cnt0;
int cntt1=cnt1,cntt0=cnt0;
if(cnt0%2){
cout<<"NO"<<endl;
continue;
}
else {
cout<<"YES"<<endl;
for(int i=0;i<n;i++)
{
if(s[i]=='1')
{
if(cntt1>cnt1/2){
cout<<"(";
cntt1--;
}
else cout<<")";
}
else if(s[i]=='0'){
if(cntt0%2)cout<<"(";
else cout<<")";
cntt0--;
}
}cout<<endl;
cntt1=cnt1,cntt0=cnt0;
for(int i=0;i<n;i++)
{
if(s[i]=='1')
{
if(cntt1>cnt1/2){
cout<<"(";
cntt1--;
}
else cout<<")";
}
else if(s[i]=='0'){
if(cntt0%2)cout<<")";
else cout<<"(";
cntt0--;
}
}
cout<<endl;
}
}
}
/*d 30myself
* 先给出最普适的情况 再把他的特殊要求加进去 重点就是一个数字应该放的位置放完了
* 其他位置爱咋放咋放 思路很简单 代码用queue 存储也很简单
*/
#include <bits/stdc++.h>
using namespace std;
queue<pair<int,int>>sa,sb;
int main() {
int n,a,b;
cin>>n;
//use a queue to store it!
if(n==1)cout<<"1 1 1"<<endl;
else {
int tmp1;
cin>>tmp1;
if(tmp1==1)a=2;
else if(tmp1==2||tmp1==3)a=1;
printf("%d 1 1\n",a);//先确定第一,第二个位置是什么数值
cin>>tmp1;
if(tmp1!=a)b=6-tmp1-a;
else {
if(a==1)b=2;
else if(a==2||a==3)b=1;
}
printf("%d 1 2\n",b);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
{
if((i==1&&j==1)||(i==1&&j==2))continue;
else if(!((i+j)%2))sa.push({i,j});//让初始的数据占据整个版面 记录它们分别可以放置的位置
else if((i+j)%2)sb.push({i,j});
}
}
for(int i=3;i<=n*n;i++)
{
cin>>tmp1;
if(tmp1!=a&&sa.size()){
int x,y;
x=sa.front().first,y=sa.front().second;
sa.pop();
printf("%d %d %d\n",a,x,y);
}
else if(tmp1!=b&&sb.size()){
int x,y;
x=sb.front().first,y=sb.front().second;
sb.pop();
printf("%d %d %d\n",b,x,y);
}
else {
if(sa.size()){
int x,y;
x=sa.front().first,y=sa.front().second;
sa.pop();
printf("%d %d %d\n",6-a-b,x,y);
}
else if(sb.size()){
int x,y;
x=sb.front().first,y=sb.front().second;
sb.pop();
printf("%d %d %d\n",6-a-b,x,y);
}
}
}
}
return 0;
}