难度:提高组
得分:40+20+0+100
[搜索]T1 计算器
给定
n
n
n 个装备,每个装备有属性
o
p
,
a
,
b
,
c
,
d
op,a,b,c,d
op,a,b,c,d ,每个不同
o
p
op
op 选最多一个,最大化
(
∑
a
+
100
)
×
(
∑
b
+
100
)
×
(
∑
c
+
100
)
×
(
∑
d
+
100
)
(\sum a+100)\times(\sum b+100)\times(\sum c+100)\times(\sum d+100)
(∑a+100)×(∑b+100)×(∑c+100)×(∑d+100)
n ≤ 50 n\leq 50 n≤50 。
赛时看出来是搜索了,粗略算了一下,以为爆搜复杂度最大是 2 n 2 2^{\frac{n}{2}} 22n ,再加上时限3s,以为稳了,结果 100 − > 40 100->40 100−>40 。算一下爆搜的实际复杂度,最大是 3 n 3 3^{\frac{n}{3}} 33n ,其实本来是可以接受的,但是却因为少了一个剪枝爆了复杂度。复杂度又算错了!若在前 n 3 \frac{n}{3} 3n 层结束了枚举,那么接下来最大是 2 n 3 \frac{2n}{3} 32n 层的空层,若跑满,复杂度退化为 3 n 3 × 2 n 3 3^{\frac{n}{3}}\times \frac{2n}{3} 33n×32n 。为了防止这种复杂度的退化,需要加入 n e x t next next 指针,直接跳到下一个不为空的地方,然后就跑得飞快了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct IO{
inline char read(){
static const int IN_LEN=1<<18|1;
static char buf[IN_LEN],*s,*t;
return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
}
template <typename _Tp> inline IO & operator >> (_Tp&x){
static char c11,boo;
for(c11=read(),boo=0;!isdigit(c11);c11=read()){
if(c11==-1)return *this;
boo|=c11=='-';
}
for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
boo&&(x=-x);
return *this;
}
inline void push(const char &c) {
putchar(c);
}
template <class T>
inline void write(T x){
if (x < 0) x = -x, push('-');
static T sta[35];
T top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
while (top) push(sta[--top] + '0');
}
template <class T>
inline void write(T x, char lastChar){
write(x),push(lastChar);
}
}io;
int n,k,ans;
struct node{
int a,b,c,d;
node(int _a=0,int _b=0,int _c=0,int _d=0){
a=_a,b=_b,c=_c,d=_d;
}
};
struct tp{
node p[55];
int pcnt;
tp(){
pcnt=0;
}
}t[55];
int nxt[55];
void dfs(int dep,int _a,int _b,int _c,int _d){
if(dep==k+1){
ans=max(ans,(100ll+_a)*(100ll+_b)*(100ll+_c)*(100ll+_d));
return;
}
for(int i=1;i<=t[dep].pcnt;++i){
dfs(nxt[dep],_a+t[dep].p[i].a,_b+t[dep].p[i].b,_c+t[dep].p[i].c,_d+t[dep].p[i].d);
}
if(t[dep].pcnt==0)
dfs(nxt[dep],_a,_b,_c,_d);
}
signed main(){
io>>n>>k;
for(int i=1;i<=n;++i){
int tpi,ai,bi,ci,di;
io>>tpi>>ai>>bi>>ci>>di;
int j=++t[tpi].pcnt;
t[tpi].p[j]=node(ai,bi,ci,di);
}
nxt[k]=k+1;
for(int i=k;i>=1;--i){
if(t[i].pcnt==0)
nxt[i-1]=nxt[i];
else
nxt[i-1]=i;
}
dfs(nxt[0],0,0,0,0);
io.write(ans,'\n');
return 0;
}
[计算几何,KMP]T2 对称轴
给定一个 n n n 边形,计算对称轴数量。多组数据, T ≤ 10 , n ≤ 1 0 5 T\leq 10,n\leq 10^5 T≤10,n≤105 。
我真没想到计算几何能和KMP,Manachar扯上关系。朴素做法 n 2 n^2 n2 枚举所有点和边,20分。
然后想改进做法,如果把点和边拆开看,一个多边形就相当于一个长为 2 n 2n 2n 的环,然后把环拆开,扩一倍,就得到长为 4 n 4n 4n 的链,在链上跑Manachar,若有某处的回文半径超过了 n n n (即回文长度超过了整个环),就计算一次答案。又或者跑KMP,在原串的反串上跑,只要长度等于 n n n 即为答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100005
#define ll long long
#define pll pair<ll, ll>
using namespace std;
struct IO{
inline char read(){
static const int IN_LEN=1<<18|1;
static char buf[IN_LEN],*s,*t;
return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
}
template <typename _Tp> inline IO & operator >> (_Tp&x){
static char c11,boo;
for(c11=read(),boo=0;!isdigit(c11);c11=read()){
if(c11==-1)return *this;
boo|=c11=='-';
}
for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
boo&&(x=-x);
return *this;
}
inline void push(const char &c) {
putchar(c);
}
template <class T>
inline void write(T x){
if (x < 0) x = -x, push('-');
static T sta[35];
T top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
while (top) push(sta[--top] + '0');
}
template <class T>
inline void write(T x, char lastChar){
write(x),push(lastChar);
}
}io;
int n,T;
struct point {
ll x,y;
point(ll xx=0, ll yy=0) {x=xx;y=yy;}
friend point operator +(point a,point b) {
return point(a.x+b.x,a.y+b.y);
}
friend point operator -(point a,point b) {
return point(a.x-b.x,a.y-b.y);
}
}a[maxn];
ll dot(point a,point b) {
return a.x*b.x+a.y*b.y;
}
ll cross(point a,point b) {
return a.x*b.y-a.y*b.x;
}
ll dist(point a,point b) {
point v=a-b;
return dot(v,v);
}
pll work_edge(int x) {
int y=x+1;
if(y>n) y=1;
return pll(dist(a[x],a[y]),1e18);
}
pll work_ang(int x) {
int y=x+1,z=x+2;
if(y>n) y=y%n;
if(z>n) z=z%n;
return pll(cross(a[y]-a[x],a[z]-a[y]),dot(a[y]-a[x],a[z]-a[y]));
}
pll edge[maxn*4],ang[maxn*4],s[maxn*4],t[maxn*4];
int nxt[maxn*4],f[maxn*4];
int KMP(int n,int m){
nxt[1]=0;
for(int i=2,j=0;i<=n;i++){
while(j>0 && t[i]!=t[j+1])
j=nxt[j];
if(t[i]==t[j+1])
j++;
nxt[i]=j;
}
for(int i=2,j=0;i<=m;i++){
while(j>0 && s[i]!=t[j+1])
j=nxt[j];
if(s[i]==t[j+1])
j++;
f[i]=j;
}
int cnt=0;
for(int i=1;i<=m;i++) {
if(f[i]==n)
cnt++;
}
return cnt;
}
int main(){
io>>T;
while(T--) {
io>>n;
for(int i=1;i<=n;i++){
int x,y;
io>>x>>y;
a[i].x=x;
a[i].y=y;
}
for(int i=1;i<=n;i++) {
edge[i]=work_edge(i);
ang[i]=work_ang(i);
}
int newn=0;
int newm=0;
for(int i=1;i<=n;i++){
s[++newn]=edge[i];
s[++newn]=ang[i];
}
for(int i=1; i<=n; i++) {
s[++newn]=edge[i];
s[++newn]=ang[i];
}
for(int i=n*2; i>=1; i--) {
t[++newm]=s[i];
}
printf("%d\n",KMP(newm,newn));
}
}
[莫反]T3 互质
给定序列 a 1 , n a_{1,n} a1,n ,在线查询 m m m 次 [ l , r ] [l,r] [l,r] 中与 x x x 互质的数。 n , m ≤ 1 0 5 n,m\leq 10^5 n,m≤105 。
[构造,猜结论]T4 签到题
给定 n + m n+m n+m 个点的二分图,在边上涂色,有 c c c 种颜色,不限数量。每个点的权值定义为边上颜色最多的数量减去边上颜色最少的数量,求最小权值。 n + m ≤ 1 0 6 n+m\leq 10^6 n+m≤106 。
名字不对劲,数据范围更不对劲,所以试图猜结论…然后就猜出来了…就是硬贪心,设边数为 p p p , a n s = ∑ [ c m o d p ! = 0 ] ans=\sum[c\,\,mod\,\,p\,\,!=0] ans=∑[cmodp!=0] 。
#include<bits/stdc++.h>
using namespace std;
struct IO{
inline char read(){
static const int IN_LEN=1<<18|1;
static char buf[IN_LEN],*s,*t;
return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
}
template <typename _Tp> inline IO & operator >> (_Tp&x){
static char c11,boo;
for(c11=read(),boo=0;!isdigit(c11);c11=read()){
if(c11==-1)return *this;
boo|=c11=='-';
}
for(x=0;isdigit(c11);c11=read())x=x*10+(c11^'0');
boo&&(x=-x);
return *this;
}
inline void push(const char &c) {
putchar(c);
}
template <class T>
inline void write(T x){
if (x < 0) x = -x, push('-');
static T sta[35];
T top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
while (top) push(sta[--top] + '0');
}
template <class T>
inline void write(T x, char lastChar){
write(x),push(lastChar);
}
}io;
int n,m,k,c;
int ans[1000005],ret;
int hs(int x){
return x+n;
}
int main(){
io>>n>>m>>k>>c;
if(c==1){
io.write(0,'\n');
return 0;
}
for(int i=1;i<=k;++i){
int u,v;
io>>u>>v;
ans[u]++,ans[hs(v)]++;
}
for(int i=1;i<=n+m;++i){
ret+=(ans[i]%c)?1:0;
}
io.write(ret,'\n');
return 0;
}