还好没打,不然掉分掉到爽-----实力还是太弱…
本题主要考察选手的英语阅读能力,我看十几分钟才看懂,呜呜呜你需要写 n n n篇博客,博客的主题相关性为一张图.
一篇博客的主题为所有相邻已写博客的主题的 mex \text{mex} mex值(不考虑0).
现在已知所有博客的主题,请你安排写作顺序.
直接模拟求 mex \text{mex} mex值即可.
int n,m,t[N],ans[N],cnt,pos[N],val[N],vis[N],num;
struct edge{int y,next;}a[N*2]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len;}
int main() {
qr(n); qr(m);
for(int i=1,x,y;i<=m;i++) qr(x),qr(y),ins(x,y),ins(y,x);
for(int i=1;i<=n;i++) qr(t[i]),val[i]=t[i];
sort(val+1,val+n+1);
for(int i=1;i<=n;i++) {
num++;
for(int k=last[i];k;k=a[k].next)
vis[t[a[k].y]]=num;
int flag=1;
while(flag<=t[i]&&vis[flag]==num) flag++;
if(flag^t[i]) puts("-1"),exit(0);
}
memset(vis+1,0,n<<2);
for(int i=1;i<=n;i++) {
if(vis[t[i]]) ++vis[t[i]];
else vis[t[i]]=lower_bound(val+1,val+n+1,t[i])-val;
ans[vis[t[i]]]=i;
}
for(int i=1;i<=n;i++) pr1(ans[i]);
return 0;
}
已知 n , p , k i ( i ∈ [ 1 , n ] ∩ N ) n,p,k_i(i\in [1,n]\cap \N) n,p,ki(i∈[1,n]∩N).
n n n个数的真实值为 p k i p^{k_i} pki.
现在需要把他们划分出2个集合.
使得两者的差最小.
转化一下,相当于每个数前加 ± \pm ±,然后最小化和(得到一个非负数)
考虑从大到小排序, s s s表示当前和(只考虑 p p p进制下的前缀位,即忽略低位)(当指数变化 Δ d \Delta d Δd时, s = s ⋅ p Δ d ) s=s\cdot p^{\Delta d}) s=s⋅pΔd).
若 s > 0 s>0 s>0,则 − . -. −.
否则 + . +. +.
当 s > n s>n s>n,则剩下的全部为 − - −.
int n,p,a[N],flag;
ll s,ans;
void del(ll &x,ll y) {x-=y; if(x<0) x+=mod;}
void add(ll &x,ll y) {x+=y; if(x>=mod) x-=mod;}
int main() {
int _;qr(_); while(_--) {
qr(n); qr(p); s=flag=ans=0;
for(int i=1;i<=n;i++) qr(a[i]);
if(p==1) {pr2(n&1); continue;}
sort(a+1,a+n+1); a[n+1]=a[n];
for(int i=n;i>=1;i--) {
int x=min(a[i+1]-a[i],20);
if(!flag) while(x) {
s*=p; x--;
if(s>=i) {flag=1;break;}
}
if(flag) {del(ans,power(p,a[i])); continue;}
if(s) s--,del(ans,power(p,a[i]));
else s++,add(ans,power(p,a[i]));
}
pr2(ans);
}
return 0;
}
给你 n n n个项链段,让你把它们合成一条项链.
端点用整数表示其美丽值.
当两点合并时,其贡献为 ⌊ l o g ( x ⊕ y ) ⌋ ( 特 别 的 , 对 于 x = y , 贡 献 为 20 ) \lfloor log(x\oplus y)\rfloor(特别的,对于x=y,贡献为20) ⌊log(x⊕y)⌋(特别的,对于x=y,贡献为20),
一个项链的美丽值为所有连接的最小值.
枚举答案 i i i,然后进行判断.
两个点能相连当且仅当其后 i i i位相等.
一条项链段相当于沟通了 x & ( 2 i − 1 ) , y & ( 2 i − 1 ) x\&(2^i-1),y \&(2^i-1) x&(2i−1),y&(2i−1).
那么我们可以把它当做一条边,然后能连成项链当且仅当所有边都能遍历(欧拉图).
所以就变成了判断欧拉图.
#include
#include
#include
#define pb push_back
#define pi pair
#define fi first
#define se second
using namespace std;
const int N=1<<20|10;
int n,a[N][2];
vector<pi> e[N];
bool v[N];
void dfs(int x) {
v[x]=1;
for(auto y:e[x])
if(!v[y.fi]) dfs(y.fi);
}
int b[N],sta[N],top,ans[N],cnt;
void Euler(int x) {
top=0; sta[++top]=x;
while(top) {
x=sta[top];
pi y; bool flag=0;
while(e[x].size()) {
y=e[x].back();
e[x].pop_back();
if(!v[y.se/2]) {flag=1; break;}
}
if(!flag) ans[++cnt]=b[top--],ans[cnt+1]=ans[cnt]^1,cnt++;
else {
v[y.se/2]=1;
++top;
b[top]=y.se;
sta[top]=y.fi;
}
}
cnt-=2;
while(cnt) printf("%d ",ans[cnt--]-1);
}
bool check(int t) {
for(int i=0;i<=t;i++)
e[i].clear(),v[i]=0;
for(int i=1;i<=n;i++) {
int x=a[i][0]&t,y=a[i][1]&t;
e[x].pb({y,2*i+1});
e[y].pb({x,2*i});
}
int c=0;
for(int i=0;i<=t;i++) {
if(e[i].size()&1) return 0;
if(e[i].size()&&!v[i]) {
dfs(i),c++;
if(c>=2) return 0;
}
}
return 1;
}
void qr(int &x) {scanf("%d",&x);}
int main() {
qr(n);
for(int i=1;i<=n;i++) qr(a[i][0]),qr(a[i][1]);
for(int i=20;~i;i--)
if(check((1<<i)-1)) {
memset(v,0,sizeof v);
printf("%d\n",i);
int t=(1<<i)-1;
for(int j=0;j<=t;j++)
if(e[j].size())
{Euler(j); break;}
break;
}
return 0;
}
给你 n n n个点.
如果两个点在从原点出发的同一射线上,那么他们的距离为其欧几里得距离.
否则,他们的距离为到原点的距离之和.
现在你需要保留 k k k个其中的点,并最大化所有无序点对的距离之和.
师兄xgc一眼就看出这是一个凸函数,然后秒了~~~,orz.
先讲讲凸函数:
对于定义在整数域上的函数 f ( x ) f(x) f(x), ∀ f ( x ) − f ( x − 1 ) ≥ f ( x + 1 ) − f ( x ) , x ∈ Z \forall f(x)-f(x-1)\ge f(x+1)-f(x),x\in \Z ∀f(x)−f(x−1)≥f(x+1)−f(x),x∈Z,则称其为凸函数.画个图/想想可以发现这是函数围成了一个上凸壳.
对于定义在实数域上的函数 f ( x ) , ∀ x ∈ R , d > 0 , f ′ ( x ) ≥ f ′ ( x + d ) f(x),\forall x\in R,d>0,f'(x)\ge f'(x+d) f(x),∀x∈R,d>0,f′(x)≥f′(x+d).
类似的,我们把 ≥ \ge ≥和 ≤ \le ≤互换一下即可得到凹函数的定义.
引理:
我们把从原点出发(不包含原点)的射线称为一条臂.
设一条臂的点数为 x x x,
当我们在这条臂上选择 ≤ min ( x , ( k + 1 ) / 2 ) \le \min(x,(k+1)/2) ≤min(x,(k+1)/2)个点,那么这些点必定在最远端,剩下的选择点必定在最近端.
证明: 设存在两个臂上相邻的点 A , B , d i s ( A , O ) > d i s ( B , O ) , l = d i s ( A , O ) − d i s ( B , O ) A,B,dis(A,O)>dis(B,O),l=dis(A,O)-dis(B,O) A,B,dis(A,O)>dis(B,O),l=dis(A,O)−dis(B,O), A A A没选, B B B选了, B B B远端还有 t t t个点的话.
那么我们把 B − > A B->A B−>A,则变化量为 l ( k − 1 − t ) − l t = l ( k − 1 − 2 t ) l(k-1-t)-lt=l(k-1-2t) l(k−1−t)−lt=l(k−1−2t),那么当单臂选择总点数 ≤ min ( x , ( k + 1 ) / 2 ) \le \min(x,(k+1)/2) ≤min(x,(k+1)/2),我们都应该选择远端.
否则,当总点数 > ( k + 1 ) / 2 >(k+1)/2 >(k+1)/2时,变化量就是负数,我们把它推得越近,答案越大.
先贴个代码再解释一下:
int n,k;
map<pi,int> s; int m;
vector<double> v[N];
double a[N],ans; int cnt;
int main() {
qr(n); qr(k);
for(int i=1,x,y,d;i<=n;i++) {
qr(x); qr(y); if(!x&&!y) continue;
d=gcd(abs(x),abs(y));
pi t=mk(x/d,y/d);
if(!s.count(t)) s[t]=++m;
int id=s[t];
v[id].push_back(sqrt(1.0*x*x+1.0*y*y));
}
a[++cnt]=0;
for(int i=1;i<=m;i++) {
sort(v[i].begin(),v[i].end(),greater<double>());
int l=0,r=SZ(v[i])-1;
double sr=0;
while(l<=r) {
double left =v[i][l]*(k-1-2*l)-sr*2;
double right=v[i][r]*(k-1-2*l)-sr*2;
if(left>=right) l++,a[++cnt]=left;
else sr+=v[i][r--],a[++cnt]=right;
}
}
sort(a+1,a+cnt+1,greater<double>());
for(int i=1;i<=k;i++) ans+=a[i];
printf("%.10lf\n",ans);
return 0;
}
重要部分是:
while(l<=r) {
double left =v[i][l]*(k-1-2*l)-sr*2;
double right=v[i][r]*(k-1-2*l)-sr*2;
if(left>=right) l++,a[++cnt]=left;
else sr+=v[i][r--],a[++cnt]=right;
}
这样的话,单臂选择数 ≤ ( k + 1 ) / 2 \le (k+1)/2 ≤(k+1)/2,则我们选择左端点(远端),否则选择右端点(近端).
在我们一直选择左边的时候, v [ i ] [ l ] , ( k − 1 − 2 l ) v[i][l],(k-1-2l) v[i][l],(k−1−2l)在减小,所以贡献在减小( Δ f ( x ) \Delta f(x) Δf(x)减小).
当开始选择右边的时候, v [ i ] [ r ] 增 加 , ( k − 1 − 2 l ) 是 负 数 且 不 断 减 小 v[i][r]增加,(k-1-2l)是负数且不断减小 v[i][r]增加,(k−1−2l)是负数且不断减小,所以贡献也在减小.
而且左边的贡献为正,右边的贡献为负.
所以这是一个凸函数.
重头戏:合并凸函数.
直接把所有 Δ f ( x ) \Delta f(x) Δf(x)放入一个数组,然后取最大的 k k k个值即可.
正确性:你取一个小的数,那么大的数一定取完了,也就是取了一个函数的合法值(对应着取了一个臂的一些点),所以正确.
给你一个强连通图,你需要找到这个图的有趣的点.
一个点 x x x是有趣的点,当且仅当它到所有点都只有一条简单路径(不含重复的点).
如果有趣的点 < n / 5
<n/5 ,那么输出-1.否则输出所有有趣的点.
判定一个点是有趣的点:
构造一棵搜索树,如果有非树枝边连向非祖先点,则不有趣.
利用概率的性质,我们先随机找到一个有趣的点.
假设随机 T = 100 T=100 T=100次,且答案不为-1,
那么都找不到的概率为 ( 4 5 ) T ≈ 2 ∗ 1 0 − 10 (\dfrac 4 5)^T\approx 2*10^{-10} (54)T≈2∗10−10,所以大可不必担心找不到(只要你的随机函数够好).
找到以后,我们定义一个点被覆盖的次数 t [ x ] t[x] t[x]为它的子树内能到达其祖先的点的个数.
如果 t [ x ] > 1 t[x]>1 t[x]>1,那么显然这个点是不有趣的,因为有两条不等的路走到祖先(注意,他们的能走到的祖先不同也没有关系).
然后我们二次扫描------根据一个点的前驱是坏点,那么这个点也是坏点这一性质.(这里我们直接把前驱定为能到达的最早的祖先是最优的)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pi pair
#define pb push_back
#define IT iterator
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=1e5+10,size=1<<20,mod=998244353;
//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
while(isdigit(c)) x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr1(o x) {
if(x<0)x=-x,putchar('-');
qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
if(x<0)x=-x,putchar('-');
qw(x); puts("");
}
int n,m,vis[N],dep[N],pre[N],t[N];
bool bad[N];
vector<int> e[N];
void clear() {
for(int i=1;i<=n;i++)
vis[i]=0,e[i].clear();
}
void out() {puts("-1\n"); clear();}
//找到一个有趣的点
bool dfs(int x) {
vis[x]=1;
for(int y:e[x])
if( vis[y]==2 || (!vis[y]&&!dfs(y)) )
return 0;
return vis[x]=2,1;
}
bool check(int x) {
memset(vis+1,0,sizeof(int[n]));
return dfs(x);
}
int Rand(int x) {
static mt19937 rnd(time(0));
return rnd()%x+1;
}
int Find() {
int T=100;
while(T--) {
int x=Rand(n);
if(check(x)) return x;
}
return -1;
}
//构造搜索树
int dfs1(int x) {
pre[x]=x; vis[x]=1; t[x]=0;
for(int y:e[x])
if(!vis[y]) {
dep[y]=dep[x]+1;
t[x]+=dfs1(y);
if(dep[pre[x]]>dep[pre[y]]) pre[x]=pre[y];
}
else {
t[x]++;
t[y]--;//差分
if(dep[pre[x]]>dep[y]) pre[x]=y;
}
bad[x]=(t[x]>1);
return t[x];
}
//二次扫描
void dfs2(int x) {
vis[x]=1;
if(bad[pre[x]]) bad[x]=1;
for(int y:e[x])
if(!vis[y]) dfs2(y);
}
int main() {
int _;qr(_); while(_--) {
qr(n); qr(m);
for(int i=1,x,y;i<=m;i++)
qr(x),qr(y),e[x].pb(y);
int x=Find();
if(x==-1) {out(); continue;}
memset(vis+1,0,sizeof(int[n]));
dep[x]=1; dfs1(x);
memset(vis+1,0,sizeof(int[n]));
dfs2(x); int cnt=0;
for(int i=1;i<=n;i++) cnt+=!bad[i];
if(cnt*5>=n) {for(int i=1;i<=n;i++) if(!bad[i]) pr1(i); puts("\n"); clear();}
else out();
}
return 0;
}