E Enigmatic Partition
找规律+二阶隔项差分
我们以 a=1 m=7 打个表
我们发现,我们需要在一次差分过后的 10,12,14,16,18 处+1,在 15,16,17,18,19处-1。如果这样枚举差分位置会导致复杂度太大,所以我们利用二阶隔项差分解决这个问题,num=a*m,我们要在 a为增加的数组,a[num+3]++,a[num+len-1+len-2+2]–。(因为是隔了两项,所以要在20处–)。b为减少的数组,b[num+len+1]++,b[num+len-1+len-2+2]–。
最后求前缀和可以把一次差分数组求出来,再求一次前缀和把每项求出来,再求一次前缀和,之和就可以利用前缀和O(1)的查询。
#include <iostream>
#include <stdio.h>
using namespace std;
const int N=3e5+10,M=1e5;
typedef long long ll;
ll a[N],b[N],c[N];
int T,l,r,x;
int main()
{
for(int i=3;i<=M;++i)
for(int j=1;i*j<=M;++j)
{
x=i*j;
a[x+3]++;
a[x+i*2-1]--;
b[x+i+1]++;
b[x+i*2-1]--;
}
for(int i=3;i<=M;++i)
a[i]+=a[i-2],b[i]+=b[i-1];
for(int i=3;i<=M;++i)
a[i]+=a[i-1]-b[i],c[i]=c[i-1]+a[i];
scanf("%d",&T);
for(int i=1;i<=T;++i)
{
scanf("%d %d",&l,&r);
printf("Case #%d: %lld\n",i,c[r]-c[l-1]);
}
return 0;
}
G Game SET
暴力枚举,因为某些神奇的结论,如果找不到n一定小于等于20,所以我们到第21就肯定能找到,这样暴力的时间复杂度就足够了。
#include <iostream>
#include <stdio.h>
#include <set>
#include <map>
using namespace std;
const int maxn=257;
int T,n;
map<string,int>mp;
struct node
{
int c[4];
}d[maxn];
char str[105];
bool check(int x,int y,int z)
{
for(int i=0;i<4;++i)
{
set<int>st;
int wild=0;
if(d[x].c[i]==-1)
wild++;
else
st.insert(d[x].c[i]);
if(d[y].c[i]==-1)
wild++;
else
st.insert(d[y].c[i]);
if(d[z].c[i]==-1)
wild++;
else
st.insert(d[z].c[i]);
if(st.size()>1&&st.size()+wild<3)
return false;
}
return true;
}
int main()
{
mp["*"]=-1;
mp["one"]=1;
mp["two"]=2;
mp["three"]=3;
mp["diamond"]=1;
mp["squiggle"]=2;
mp["oval"]=3;
mp["solid"]=1;
mp["striped"]=2;
mp["open"]=3;
mp["red"]=1;
mp["green"]=2;
mp["purple"]=3;
scanf("%d",&T);
for(int cas=1;cas<=T;++cas)
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%s",str);
int j=1;
for(int p=0;p<4;++p)
{
string a;
for(j;str[j]!=']';++j)
{
a+=str[j];
}
d[i].c[p]=mp[a];
j+=2;
}
}
bool flag=0;
int i,j,k;
for(i=1;i<=n;++i)
{
for(j=i+1;j<=n;++j)
{
for(k=j+1;k<=n;++k)
{
if(check(i,j,k))
{
flag=1;
break;
}
}
if(flag)
break;
}
if(flag)
break;
}
if(!flag)
printf("Case #%d: -1\n",cas);
else
{
printf("Case #%d: %d %d %d\n",cas,i,j,k);
}
}
return 0;
}
I Interesting Computer Game
我们发现,不必顺次贪心去取,因为这和次序无关,我们全局的去看,每次选只剩一个的。如果此时都不是只剩一个的话,就都可以要。我们可以对应成图,两个选项间连边,如果一个连通图是环就都可以选上,如果不是环,则会少选一个,ans–。具体利用拓扑实现即可。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=1e5+5;
int T;
int n,cnt,tot;
struct node
{
int next,to;
}edge[maxn<<2];
int head[maxn<<1];
int ing[maxn<<1];
void add(int x,int y)
{
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
}
void init()
{
cnt=0;
tot=0;
memset(head,0,sizeof(head));
memset(ing,0,sizeof(ing));
memset(edge,0,sizeof(edge));
}
queue<int>q;
int a[maxn],b[maxn],c[maxn<<1];
int main()
{
scanf("%d",&T);
for(int k=1;k<=T;++k)
{
init();
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d %d",&a[i],&b[i]);
c[++cnt]=a[i];
c[++cnt]=b[i];
}
sort(c+1,c+1+cnt);
int size=unique(c+1,c+1+cnt)-c-1;
for(int i=1;i<=n;++i)
{
int aa=lower_bound(c+1,c+1+size,a[i])-c;
int bb=lower_bound(c+1,c+1+size,b[i])-c;
ing[aa]++;
ing[bb]++;
add(aa,bb);
add(bb,aa);
}
int ans=size;
for(int i=1;i<=n;++i)
{
int aa=lower_bound(c+1,c+1+size,a[i])-c;
int bb=lower_bound(c+1,c+1+size,b[i])-c;
if(ing[aa]==1&&ing[bb]==1)
ans--;
else
if(ing[aa]==1)
q.push(aa);
else
if(ing[bb]==1)
q.push(bb);
}
while(!q.empty())
{
int u=q.front();
ing[u]--;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(ing[v]==1)
ans--;
else
if(ing[v]>1)
{
ing[v]--;
if(ing[v]==1)
q.push(v);
}
}
q.pop();
}
printf("Case #%d: %d\n",k,ans);
}
return 0;
}
K Kabaleo Lite
贪心,先求一个前缀和,每次选可选的里面最大的,更新答案即可。注意会爆long long且有负数,我们可以用大数,或者__int128。
#include <bits/stdc++.h>
using namespace std;
const __int128 inf=1e5+5;
const int maxn=1e5+5;
inline __int128 read()
{
__int128 x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline void write(__int128 x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
struct node
{
__int128 value;
int id;
int pos;
}c[maxn];
int T,n;
__int128 a[maxn],b[maxn];
bool cmp(node x,node y)
{
if(x.value==y.value)
return x.pos>y.pos;
else
return x.value>y.value;
}
int main()
{
b[0]=inf;
scanf("%d",&T);
for(int k=1;k<=T;++k)
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
a[i]=read();
a[i]+=a[i-1];
c[i].value=a[i];
c[i].id=i;
}
for(int i=1;i<=n;++i)
{
b[i]=read();
if(b[i]<b[i-1])
{
c[i].pos=i;
}
else
{
b[i]=b[i-1];
c[i].pos=c[i-1].pos;
}
}
sort(c+1,c+1+n,cmp);
__int128 ans=0;
int last=n;
__int128 res=0;
for(int i=1;i<=n;++i)
{
if(c[i].id<=last)
{
last=c[i].pos-1;
ans+=c[i].value*(b[c[i].id]-res);
res+=b[c[i].id]-res;
}
}
printf("Case #%d: %d ",k,b[1]);
write(ans);
printf("\n");
}
return 0;
}