A
思路
当中间点确定,左侧的长度与右侧的长度乘积就会贡献一次答案,前提是左右两个与中间点之间的的个数相等。分别预处理出每个左侧和右侧连续的长度,考虑对每个长度计算贡献。
对于每个,左侧紧挨的与右侧紧挨的单独计算,之后以第一个为例,第一个会分别与第个及其后边紧挨的贡献一次答案(当第个为中间点时),与第个贡献一次答案(当第个为中间点时),以此类推。可以发现对于下标来说,都是奇数在一起贡献答案,偶数在一起贡献答案,因此可以维护两个后缀和来优化。
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
}
vector<ll> lft;
ll n;
string s;
void print()
{
for(auto k:lft)
cout<<k<<" ";
puts("\n");
}
void solve()
{
In(1,&n);
cin>>s;
s=' '+s;
ll cnt=0,sum1=0,sum2=0,ans=0;
lft.clear();
rep(i,1,n)
{
if(s[i]=='0')
++cnt;
else
{
lft.emplace_back(cnt);
if(lft.size()&1)
sum1+=cnt+1;
else
sum2+=cnt+1;
cnt=0;
}
}
lft.emplace_back(cnt);
if(lft.size()&1)
sum1+=cnt+1;
else
sum2+=cnt+1;
sum1-=lft[0]+1;
rep(i,0,(int)lft.size()-2)
{
ans+=lft[i]*lft[i+1];
if((i&1)==0)
{
sum2-=lft[i+1]+1;
ans+=(lft[i]+1)*sum2;
}
else
{
sum1-=lft[i+1]+1;
ans+=(lft[i]+1)*sum1;
}
}
Out(1,ans);
}
int main()
{
int _;
cin>>_;
while(_--)
{
solve();
}
}
F
思路
每个猴子的最终状态由它听到的最后一个笑话决定,如果这个笑话听了恰好次,它就在地上,否则就在座位上。
先求出每个猴子听到的最后一个笑话。利用差分,分别标记一下每个笑话的左右端点。
每次碰到一个左端点,就把这个笑话的标号(也就是被讲的次序)扔到优先队列中,并增加一次这类笑话的出现次数(注意这里“个”和“类”的区别),把这个笑话标记为入队,表示之后的猴子都会受到这个笑话的影响。因为要找听到的最后一个笑话,因此优先队列中的元素按照笑话的标号从大到小排序,越晚讲的笑话排序越靠前,这样队首元素就是当前猴子听到的最后一个笑话。
每次碰到右端点,就减少一次这类笑话的出现次数,并把它标记为不在队列中。如果在之后某个笑话出现在队首但是被标记为已经出队,就可以直接把它pop掉(毕竟不可以直接在priority_queue内部删除元素)。
确定每个猴子听到的最后一个笑话的同时,因为还维护了这类笑话的出现次数,所以可以直接根据这个出现次数维护答案。
另外一种思路是用线段树搞,对于每次讲笑话,区间修改一下这些猴子听到的笑话,这样最后的树存储的就是每个猴子听到的最后一个笑话。之后单独考虑每一类笑话,每次用一颗线段树维护下每个猴子听这类笑话的次数,如果这是这个猴子听的最后一个笑话且它听了次或次以上,就说明这个猴子在地上,反之就在座位上。
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
}
const ll N=1E5+10;
ll n,m;
ll ma[N],type[N],inq[N];
vector<ll> a[N];
priority_queue<ll> q;
void print()
{
rep(i,1,n)
cout<<ma[i]<<" ";
cout<<endl;
}
void solve()
{
In(2,&n,&m);
rep(i,1,n+1)
a[i].clear();
rep(i,1,m)
inq[i]=0;
while(!q.empty())
q.pop();
rep(i,1,m)
{
ll x,ty,len;
In(3,&x,&ty,&len);
ll l=max(1ll,x-len),r=min(n,x+len)+1;
ma[ty]=0;
type[i]=ty;
a[l].emplace_back(i);
a[r].emplace_back(-i);
}
ll ans=0;
rep(i,1,n)
{
for(auto k:a[i])
{
if(k>0)
{
q.push(k);
ma[type[k]]++;
inq[k]=1;
}
else
{
ma[type[-k]]--;
inq[-k]=0;
}
}
while(!q.empty() && !inq[q.top()])
q.pop();
if(q.empty() || ma[type[q.top()]]!=1)
++ans;
}
Out(1,ans);
}
int main()
{
int _;
cin>>_;
while(_--)
{
solve();
}
}
G
思路
对于每个四边形,当左上角坐标为时,右下角的坐标有种选法(包括两个坐标重合的情况)。因此对于规模为的四边形,小四边形的种数为。根据等差数列求和公式化简一下可以得到答案。
用所有可能的情况减去不合法的情况就是答案。如果一个雷位于,那么所有左上角在雷左上角,右下角在雷右下角的四边形都是不合法的(包括某个角与雷重合的情况)。显然只考虑这一个雷的时候不合法的情况有种。直接减去即可。
当有多个雷的时候,这样减会有重复的,因此用容斥原理。
雷的个数只有,二进制枚举每一种组合,如果这个组合中雷的个数为奇数,就从答案中减去他们的并集,否则就加上。
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
}
const ll N=25;
ll n,m,k;
ll x[N],y[N];
ll check(ll sta)
{
ll cnt=0,ret=0;
ll lx=LLM,ly=LLM,rx=0,ry=0;
vector<pii> a;
rep(i,0,k-1)
{
if((1<<i)&sta)
{
++cnt;
lx=min(lx,x[i+1]);
rx=max(rx,x[i+1]);
ly=min(ly,y[i+1]);
ry=max(ry,y[i+1]);
}
}
ret=lx*ly*(n-rx+1)*(m-ry+1);
return ret*(cnt&1?1:-1);
}
void solve()
{
cin>>n>>m>>k;
ll ans=0;
rep(i,1,k)
{
cin>>x[i]>>y[i];
}
ll sum=m*(m+1)/2;
ans=n*sum+n*(n-1)/2*sum;
ll lim=1<<k;
rep(i,1,lim-1)
{
ans-=check(i);
}
Out(1,ans);
}
int main()
{
int _;
cin>>_;
while(_--)
{
solve();
}
}