A. Odd One Out
#include
#define endl '\n'
#define int long long
using namespace std;
void solve() {
int a,b,c;
cin>>a>>b>>c;
map<int,int>mp;
mp[a]++;
mp[b]++;
mp[c]++;
for(auto v:mp){
if(v.second==1){
cout<<v.first<<endl;
return;
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
B. Not Quite Latin Square
#include
#define endl '\n'
#define int long long
using namespace std;
void solve() {
map<char,int>mp;
for(int i=0;i<9;i++){
char ch;
cin>>ch;
mp[ch]++;
}
for(auto v:mp){
if(v.second==2){
cout<<v.first<<endl;
return;
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
C. Can I Square?
#include
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {
cin>>n;
int sum=0;
for(int i=1;i<=n;i++){
int x;
cin>>x;
sum+=x;
}
if((int)sqrt(sum)*(int)sqrt(sum)==sum) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
D. Unnatural Language Processing
长度为n的字符串(均为小写字母a,b,c,d,e)
将单词拆分成音节
CV.CVC.CVC
#include
#define endl '\n'
#define int long long
using namespace std;
int n;
string s;
void solve() {
cin>>n;
cin>>s;
map<char,int>mp1,mp2;
mp1['a']=1;
mp1['e']=1;
mp2['b']=1;
mp2['c']=1;
mp2['d']=1;
for(int i=0;i<n;i++){
cout<<s[i];
if(mp1[s[i]]==1&&mp2[s[i+1]]==1&&mp1[s[i+2]]==1) cout<<'.';
else if(mp2[s[i]]==1&&mp2[s[i+1]]==1) cout<<'.';
}
cout<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
E. Romantic Glasses
一共有n个杯子
ai表示第i个杯子装了多少果汁
问能否使得[l,r]中奇数位总量和偶数位总量相同
两者总量相同,利用奇数位加,偶数位减,看是否有为0的区间
#include
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {
cin>>n;
map<int,int>mp;
for(int i=1;i<=n;i++) cin>>a[i];
int sum=0;
for(int i=1;i<=n;i++){
if(i%2==1) sum+=a[i];
else sum-=a[i];
if(sum==0||mp[sum]==1){
cout<<"Yes"<<endl;
return;
}
mp[sum]=1;
}
cout<<"No"<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
F. Greetings
一共有n个人
ai代表第i个人的起始位置,bi代表第i个人的结束位置(所有a,b数组的数均不同)
ai小于bi
均匀速
不存在追的上,只有它的终点大于(不存在等于)另一个时,才会相遇
通过手玩发现,包含和被包含关系才会相遇一次
trick:
这就作为一个案例,当遇到统区间包含的问题时,可以有以下几种方法
枚举左端点(从小到大),一边枚举,一边把枚举过的区间的右端点放到set里,看有几个右端点是大于当前右端点的(利用set的二分upperbound),就是有几个区间包含它,就打几次招呼,set中自带的二分函数时间复杂度为O(logn),这样总的时间复杂度是O(nlogn),照理说,这样是没问题的,但是有一个问题就是set自带的二分函数返回的是迭代器,然后想要获得位置的话需要distance(迭代器1,迭代器2),它底层实现是遍历,也就是O(n),这样时间复杂度就超了,试了一下直接用迭代器减s.begin()是会报错的,这个方法就不能用了
#include
#define endl '\n'
//#define int long long
using namespace std;
const int N=2e5+10;
int l[N],r[N];
int n;
struct node{
int l,r;
bool operator<(const node &W)const{
return l<W.l;
}
}q[N];
void solve() {
cin>>n;
for(int i=1;i<=n;i++) cin>>q[i].l>>q[i].r;
sort(q+1,q+1+n);
set<int>s;
int ans=0;
s.insert(q[1].r);
for(int i=2;i<=n;i++){
auto t=s.upper_bound(q[i].r);
int d=distance(s.begin(),t);
ans+=s.size()-d;
s.insert(q[i].r);
}
cout<<ans<<endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
于是改用树状数组,但是有一个问题就是数的范围太大了,达到了1e9,要想用树状数组的话,这些数值本身作为一个位置,空间肯定爆了,所幸数的个数比较小,于是先离散化,这样的话空间就不会爆了
#include
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int l[N],r[N];
int tr[N];
int n;
vector<int>alls;
int find(int x){
int l=0,r=alls.size()-1;
while(l<r){
int mid=(l+r)/2;
if(alls[mid]>=x) r=mid;
else l=mid+1;
}
return l+1;
}
int lowbit(int x)
{
return x & -x;
}
int sum(int x)//求x位置前的前缀和
{
int res=0;
while(x) res+=tr[x],x-=lowbit(x);
return res;
}
void add(int x,int c)//在x的位置上加上常数c
{
while(x<=n) tr[x]+=c,x+=lowbit(x);
}
struct node{
int l,r;
bool operator<(const node &W)const{
return l<W.l;
}
}q[N];
void solve() {
cin>>n;
memset(tr,0,sizeof tr);
for(int i=1;i<=n;i++) cin>>q[i].l>>q[i].r;
//离散化
alls.clear();
for(int i=1;i<=n;i++){
alls.push_back(q[i].r);
}
sort(alls.begin(),alls.end());
sort(q+1,q+1+n);
int ans=0;
int pos=find(q[1].r);
add(pos,1);
for(int i=2;i<=n;i++){
pos=find(q[i].r);
int d=sum(pos);
ans+=i-1-d;
add(pos,1);
}
cout<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
还有其它的方法,就是我们只要将左端点从小到大排序,然后发现,只要右端点逆序就存在一个包含区间(就是说如果左端点小,但是右端点大,那么它就把一个区间包含住了),所以只要统计逆序对的数量即可,用归并排序
#include
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int l[N],r[N];
int c[N];
int n;
int ans;
struct node{
int l,r;
bool operator<(const node &W)const{
return l<W.l;
}
}q[N];
void mergesort(int l,int r){
if(l==r) return;
int mid=(l+r)/2;
mergesort(l,mid),mergesort(mid+1,r);
int i=l,j=mid+1,k=0;
while(i<=mid&&j<=r){
if(q[i].r<=q[j].r) c[k++]=q[i++].r;
else{
c[k++]=q[j++].r;
ans+=mid-i+1;
}
}
while(i<=mid) c[k++]=q[i++].r;
while(j<=r) c[k++]=q[j++].r;
for(int i=l,k=0;i<=r;i++) q[i].r=c[k++];
}
void solve() {
cin>>n;
for(int i=1;i<=n;i++) cin>>q[i].l>>q[i].r;
sort(q+1,q+1+n);
ans=0;
mergesort(1,n);
cout<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}