题目链接

C - Flippy Sequence(组合数学+分类讨论)

两区间异或一下,分段考虑,如果全为0则任选两相同区间,答案为$C_{n+1}^{2}=\frac{n(n+1)}{2}$,只有一段连续的1则两区间有一个公共边界,另外两个边界分别为连续1的左右边界,答案为$2C_{n-1}^{1}=2(n-1)$,有两段则两区间平分四个边界,答案为$C_{4}^{2}=6$,三段以上无解。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+;
char a[N],b[N];
int n;
ll ans;
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%s%s",&n,a,b);
for(int i=; i<n; ++i)a[i]=a[i]==b[i]?'':'';
int t=;
for(int i=; i<n; ++i)if(a[i]==''&&a[i+]!='')++t;
if(t==)ans=(ll)n*(n+)/;
else if(t==)ans=(ll)(n-)*;
else if(t==)ans=;
else ans=;
printf("%lld\n",ans);
}
return ;
}

D - Magic Multiplication(数学规律+构造)

只要发现一个规律即可:只要A的第一个数确定了,那么剩下的数都可以推出来,而且推的过程中C中不可能出现一位和两位数都可行的情况,因此没有回溯的过程,直接枚举A的第一个数然后判断是否可行就行了。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+,mod=1e9+,inf=0x3f3f3f3f;
int na,nb,nc,p,a[N],b[N],c[N];
char s[N];
bool Set(int x) {
a[]=x,p=;
for(int i=; i<nb; ++i) {
if(p<nc&&c[p]%a[]==&&c[p]/a[]<=)b[i]=c[p]/a[],p+=;
else if(p+<nc&&(c[p]*+c[p+])%a[]==&&(c[p]*+c[p+])/a[]<=)b[i]=(c[p]*+c[p+])/a[],p+=;
else return ;
}
return ;
}
bool check() {
for(int i=; i<na; ++i) {
if(p<nc&&c[p]%b[]==&&c[p]/b[]<=)a[i]=c[p]/b[],p+=;
else if(p+<nc&&(c[p]*+c[p+])%b[]==&&(c[p]*+c[p+])/b[]<=)a[i]=(c[p]*+c[p+])/b[],p+=;
else return ;
for(int j=; j<nb; ++j) {
if(p<nc&&a[i]*b[j]==c[p])p+=;
else if(p+<nc&&a[i]*b[j]==c[p]*+c[p+])p+=;
else return ;
}
}
return p==nc;
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d",&na,&nb);
scanf("%s",s),nc=strlen(s);
for(int i=; i<nc; ++i)c[i]=s[i]-'';
bool f=;
for(int i=; i<=; ++i) {
if(Set(i)&&check()) {
f=;
for(int i=; i<na; ++i)printf("%d",a[i]);
printf(" ");
for(int i=; i<nb; ++i)printf("%d",b[i]);
puts("");
break;
}
}
if(!f)puts("Impossible");
}
return ;
}

E - Plants vs. Zombies(二分+贪心)

二分答案,然后从左往右走,如果遇到一个点处的值没有满足条件,则在它和它后面的数之间来回走动,判断所需步数是否小于等于m即可。

注意即使当前的数已经满足条件了,也要往后走一步(最后一格除外)

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n;
ll m,a[N],b[N];
bool ok(ll x) {
ll cnt=m;
for(int i=; i<n; ++i)a[i]=;
for(int i=; i<n; ++i) {
if(a[i]<x) {
ll t=(x-a[i]-)/b[i]+;
a[i]+=b[i]*t,a[i+]+=b[i+]*(t-);
cnt-=*t-;
} else {
if(i==n-)return ;
cnt--;
}
if(cnt<)return ;
}
return cnt>=;
}
ll bi(ll l,ll r) {
while(l<r) {
ll mid=(l+r)>>;
ok(mid+)?l=mid+:r=mid;
}
return l;
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%lld",&n,&m);
for(ll i=; i<n; ++i)scanf("%lld",&b[i]);
printf("%lld\n",bi(,inf));
}
return ;
}

F - Tournament(找规律+构造)

用分治法打出循环赛日程表,然后找规律,发现最多可进行lowbit(n)-1场比赛,依次输出即可。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,mod=1e9+,inf=0x3f3f3f3f;
int a[N][N],n,k;
int main() {
for(int k=; k<; k<<=)
for(int i=; i<k; ++i)
for(int j=; j<k; ++j) {
a[i+k][j]=a[i][j+k]=a[i][j]+k;
a[i+k][j+k]=a[i][j];
}
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d",&n,&k);
if(k>=(n&-n))puts("Impossible");
else {
for(int i=; i<=k; ++i)
for(int j=; j<n; ++j)printf("%d%c",a[i][j]+," \n"[j==n-]);
}
}
return ;
}

也可以利用异或的性质,将一个排列中的每个数异或上一个相同的数可以实现一定范围内的循环节长度为2的置换,取异或的数分别为1,2,3,...,k分别对应第1,2,3,...,k轮的比赛即可。(不知道为什么这样做答案是正确的)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,mod=1e9+,inf=0x3f3f3f3f;
int n,k;
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d",&n,&k);
if(k>=(n&-n))puts("Impossible");
else {
for(int i=; i<=k; ++i)
for(int j=; j<n; ++j)printf("%d%c",(j^i)+," \n"[j==n-]);
}
}
return ;
}

G - Repair the Artwork(容斥+dp)

这题卡常比较厉害,我一开始用bfs+循环队列的写法怎么写都是T,后来参考了榜单上大神的代码才AC的。

从左往右递推,设dp[i][j]为递推到第i个数时,可选区间数量为j的方案总数。
先考虑只包含0和1的情况,在这种情况下,每碰到一个1,设这个1的位置为i,它前面的1的位置为i’,则可选区间数量就要加上$C_{i-i'-1}^{2}=\frac{(i-i'-1)(i-i')}{2}$
再考虑包含2的情况,对于每个2,可选区间数为(可选它也可不选它的方案数)-(必须选它的方案数),利用容斥的思想, 可以把每个2拆成一个0和一个1,对答案的贡献即为把它当做0时的贡献减去把它当做1时的贡献。
复杂度$O(n^4)$

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,mod=1e9+;
int dp[N][N*(N+)/],vis[N][N*(N+)/],a[N],n,m,ka;
int Pow(int a,int b) {
int ret=;
for(; b; b>>=,a=(ll)a*a%mod)if(b&)ret=(ll)ret*a%mod;
return ret;
}
void upd(int i,int j,int x) {
if(vis[i][j]!=ka)vis[i][j]=ka,dp[i][j]=;
dp[i][j]=(dp[i][j]+x)%mod;
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d",&n,&m),++ka;
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
a[n+]=,upd(,,);
for(int i=; i<=n; ++i)
for(int j=; j<=i*(i+)/; ++j)if(vis[i][j]==ka&&dp[i][j])
for(int k=i+; k<=n+; ++k) {
if(a[k]==) {upd(k,j+(k-i)*(k-i-)/,dp[i][j]); break;}
else if(a[k]==)upd(k,j+(k-i)*(k-i-)/,-dp[i][j]);
}
ll ans=;
for(int i=; i<=n*(n+)/; ++i)if(vis[n+][i]==ka)ans=(ans+(ll)dp[n+][i]*Pow(i,m))%mod;
ans=(ans+mod)%mod;
printf("%lld\n",ans);
}
return ;
}

I - Soldier Game(排序+枚举+线段树)

将所有可选划分区间按权值从小到大排序,依次枚举每个区间,将该区间的权值作为最小值,然后再线段树上查询最大值即可。每枚举完一个区间,将这个区间删掉(去掉它在线段树上的贡献)。
对于每个区间,设m为不包含左右边界元素的划分的最大权值,lm为不包含右边界元素的划分的最大权值,mr为不包含左边界元素的划分的最大权值,lmr为包含左右边界元素的划分的最大权值,则可以在$O(1)$的时间内将两区间的信息进行合并。合并时,考虑两区间交界处包含左区间右边界和右区间左边界的区间的贡献即可。
每删除一个区间,线段树上只有包含该区间的区间会受到影响,因此更新的复杂度为$O(logn)$。
总复杂度$O(nlogn)$

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll a[N][];
int n,n2;
struct Seg {ll m,lm,mr,lmr;} s[N<<];
struct P {
int x,y;
bool operator<(const P& b)const {return a[x][y]<a[b.x][b.y];}
} b[N<<];
#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
void pu(int u,int l,int r) {
s[u].m=min(max(a[mid][],max(s[ls].m,s[rs].m)),max(s[ls].mr,s[rs].lm));
s[u].lm=min(max(a[mid][],max(s[ls].lm,s[rs].m)),max(s[ls].lmr,s[rs].lm));
s[u].mr=min(max(a[mid][],max(s[ls].m,s[rs].mr)),max(s[ls].mr,s[rs].lmr));
s[u].lmr=min(max(a[mid][],max(s[ls].lm,s[rs].mr)),max(s[ls].lmr,s[rs].lmr));
}
void build(int u=,int l=,int r=n-) {
if(l==r) {s[u].m=inf,s[u].lm=s[u].mr=~inf,s[u].lmr=a[l][]; return;}
build(ls,l,mid),build(rs,mid+,r),pu(u,l,r);
}
void upd(int p,int u=,int l=,int r=n-) {
if(l==r) {s[u].m=inf,s[u].lm=s[u].mr=~inf,s[u].lmr=a[l][]; return;}
p<=mid?upd(p,ls,l,mid):upd(p,rs,mid+,r),pu(u,l,r);
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d",&n);
for(int i=; i<n; ++i)scanf("%lld",&a[i][]);
for(int i=; i<n-; ++i)a[i][]=a[i][]+a[i+][];
a[n-][]=inf;
build();
n2=;
for(int i=; i<n; ++i)b[n2++]= {i,},b[n2++]= {i,};
sort(b,b+n2);
ll ans=inf;
for(int i=; i<n2&&s[].lmr!=inf; ++i) {
ans=min(ans,s[].lmr-a[b[i].x][b[i].y]);
a[b[i].x][b[i].y]=inf,upd(b[i].x);
}
printf("%lld\n",ans);
}
return ;
}

J - Books(思维)

先将0元的物品全部买下,如果剩下的m<0或m>n则输出Impossible,m=n则输出Richman,其他情况的最优解释将前m本全部买下,再加上后面的最小值-1。证明比较简单。

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
int n,m,a[N];
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d",&n,&m);
for(int i=; i<n; ++i) {
scanf("%d",&a[i]);
if(!a[i])i--,n--,m--;
}
if(m<||m>n)puts("Impossible");
else if(m==n)puts("Richman");
else printf("%lld\n",accumulate(a,a+m,0ll)+*min_element(a+m,a+n)-);
}
return ;
}

K - Airdrop(思维+排序+扫描)

将所有点按x坐标从小到大排序,分别算出作为(x0,y0)左边的点和右边的点时到(x0,y0)的曼哈顿距离(相对的),然后从左往右扫一遍x值,每扫到一个x值,将所有是这个x值的点的曼哈顿距离加进去,如果之前没有在这个距离上存活的点,则这个点存活,否则这个距离上的点全部完蛋,枚举的同时记录到每个x值时有多少存活的点。然后再从右往左扫一遍。最后再扫一遍x值,取左边存活的点+右边存活的点+横坐标为x的点的最大值作为答案。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+,inf=0x3f3f3f3f;
struct P {
int x,y;
bool operator<(const P& b)const {return x<b.x;}
} p[N];
int n,Y,dis[][N],f[N<<],cnt[][N],mix,mxx;
int main() {
int T;
for(scanf("%d",&T); T--;) {
memset(cnt,,sizeof cnt);
scanf("%d%d",&n,&Y);
for(int i=; i<=n; ++i)scanf("%d%d",&p[i].x,&p[i].y),p[i].x++;
sort(p+,p++n),mix=p[].x,mxx=p[n].x;
for(int i=; i<=n; ++i) {
dis[][i]=abs(Y-p[i].y)-p[i].x+;
dis[][i]=abs(Y-p[i].y)+p[i].x;
}
memset(f,,sizeof f);
for(int i=mix,j=,k; i<=mxx; ++i) {
cnt[][i]=cnt[][i-];
k=j;
for(; j<=n&&p[j].x==i; ++j) {
if(f[dis[][j]]==)f[dis[][j]]=,cnt[][i]++;
else if(f[dis[][j]]==)f[dis[][j]]=-,cnt[][i]--;
}
j=k;
for(; j<=n&&p[j].x==i; ++j)if(f[dis[][j]]==-)f[dis[][j]]=;
}
memset(f,,sizeof f);
for(int i=mxx,j=n,k; i>=mix; --i) {
cnt[][i]=cnt[][i+];
k=j;
for(; j>=&&p[j].x==i; --j) {
if(f[dis[][j]]==)f[dis[][j]]=,cnt[][i]++;
else if(f[dis[][j]]==)f[dis[][j]]=-,cnt[][i]--;
}
j=k;
for(; j>=&&p[j].x==i; --j)if(f[dis[][j]]==-)f[dis[][j]]=;
}
for(int i=; i<=n; ++i)cnt[][p[i].x]++;
int ans1=~inf,ans2=inf;
for(int i=mix-; i<=mxx+; ++i) {
ans1=max(ans1,cnt[][i-]+cnt[][i+]+cnt[][i]);
ans2=min(ans2,cnt[][i-]+cnt[][i+]+cnt[][i]);
}
printf("%d %d\n",ans2,ans1);
}
return ;
}

L - Sub-cycle Graph(组合数学+生成函数)

一个连接n个点的环包含m条边的子图中,一定包含k=n-m个连通分量,其中每个连通分量要么是一个点,要么是包含多个点的一条链,则问题转化成把n个顶点划分成k个连通分量的方案数。我们可以对所有点进行染色,将同一连通分量中的点染成相同的颜色,则问题转化成将n个点染成k种染色的方案数(注意是带权的方案数,因为将一些点染成相同的颜色(放进同一个连通分量)的方法有多种)。

用k种颜色给n个点染色的生成函数(指数型)为

$f(x)=\sum_{i=1}^{k}\frac{a_i}{i!}x^i,a_i=\left\{\begin{matrix}\begin{aligned}&1,i=1\\&\frac{i!}{2},i>1\end{aligned}\end{matrix}\right.$

其中ai的含义是将i个点染成同一颜色(放进同一连通分量)的方案数。

上式化简得:

$f(x)=\frac{1}{2}(2x+x^2+x^3+...+x^k)=\frac{1}{2}x(\frac{2-x}{1-x})$

则答案的生成函数为:

$[f(x)]^k=\frac{1}{2^k}x^k(2-x)^k\frac{1}{(1-x)^k}$

所求的结果即为上式中x^n项的系数乘上n!再除以k!(因为各种颜色是没有差别的,需要去序)。

其中$(2-x)^k$中项的系数可以用二项式定理求,而后面的$\frac{1}{(1-x)^k}$比较特殊,与这个函数相乘,相当于对整个多项式求了k次前缀和,自己算一算就会发现,求k次前缀和后第n项的系数为

$\sum_{i=0}^{n}C_{k-1+n-i}^{k-1}a_i$

因此每次可以在O(n)的时间内算出答案。

注意k=0和k<0的情况要特殊处理。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5+,inf=0x3f3f3f3f,mod=1e9+;
ll a[N],n,k,f[N],invf[N],inv[N],ans,invp2[N];
ll C(ll a,ll b) {return f[a]*invf[b]%mod*invf[a-b]%mod;}
int main() {
f[]=invf[]=f[]=invf[]=,inv[]=;
for(ll i=; i<N; ++i) {
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
f[i]=f[i-]*i%mod,invf[i]=invf[i-]*inv[i]%mod;
}
invp2[]=;
for(ll i=; i<N; ++i)invp2[i]=invp2[i-]*inv[]%mod;
ll T;
for(scanf("%lld",&T); T--;) {
scanf("%lld%lld",&n,&k),k=n-k;
if(k<)ans=;
else if(k==)ans=f[n-]*inv[]%mod;
else {
ans=;
for(ll i=; i<=n; ++i)a[i]=;
for(ll i=; i<=k&&i+k<=n; ++i) {
a[i+k]=C(k,i)*invp2[i]%mod;
if(i&)a[i+k]=-a[i+k];
}
for(ll i=; i<=n; ++i)ans=(ans+C(k-+n-i,k-)*a[i])%mod;
ans=(ans*f[n]%mod*invf[k]%mod+mod)%mod;
}
printf("%lld\n",ans);
}
return ;
}

M - Function and Function(循环节)

算到0的时候直接根据k值的奇偶出结果就行,注意n=0的时候需要特判。

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int a[]= {,,,,,,,,,};
int n,k;
int ff(int n) {
if(!n)return ;
int ret=;
for(; n; n/=)ret+=a[n%];
return ret;
}
int f(int n,int k) {
while(k--) {
n=ff(n);
if(!n)return k&;
}
return n;
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d",&n,&k);
printf("%d\n",f(n,k));
}
return ;
}

Reference:https://www.cnblogs.com/qswg/p/9930006.html

2018 ACM-ICPC 区域赛(青岛站)题解整理的更多相关文章

  1. 第42届亚洲区域赛青岛站(2017icpc青岛)经验总结以及一些感想

    上一次写这种东西还是天梯赛,当时打完心里也是挺激动的,然后我们队也没有去参加省赛,但是过了一段时间我还是从那里面恢复了出来.因为我当时确实还是很菜的,当时连个暴力都不会,看着自己仅过的那些百度的题目确 ...

  2. 2019年icpc区域赛银川站总结

    目录 一.前言 二.10月19日热身赛 三.10月20日正式赛 四.结果 一.前言 比赛前我们队有ccpc厦门和icpc银川的名额,然而这两个地区的时间正好撞了,考虑到银川更容易拿奖,加上我们ACM协 ...

  3. 2014年acm亚洲区域赛·鞍山站

    今天北京赛站的比赛也结束了···看了一天的直播之后意识到鞍山站的比赛都过去了一个多月了···这一个月比较萎靡···整天都在睡觉写报告画工图中度过··· 鞍山比哈尔滨还是暖和很多的···就是山上有奇怪的 ...

  4. 2018 ACM-ICPC 亚洲区域赛青岛现场赛 —— Problem F. Tournament

    题面:http://acm.zju.edu.cn/contest-materials/qd2018/qd2018_problems.pdf 题意: n个骑士决斗K轮 要求是每个骑士只能跟另外一个骑士决 ...

  5. 近几年ACM/ICPC区域赛铜牌题

    2013 changsha zoj 3726 3728 3736 3735 2013 chengdu hud 4786 4788 4790 2013 hangzhou hdu 4770 4771 47 ...

  6. 2016 ACM-ICPC 区域赛(大连站)题解

    题目链接 A - Wrestling Match (二分图染色) 题意略坑(没有说好的玩家一定能打过差的玩家啊啊~~) 典型的二分图染色问题,每个玩家看成一个点,把相互较量过的玩家之间连边,好的玩家染 ...

  7. 2016 ACM/ICPC 区域赛(北京) E 题 bfs

    https://vjudge.net/problem/UVALive-7672 题意    输入一个五位数n 问由12345变到n的操作最少次数 不可达输出-1 有三种操作 1.交换相邻的位置 次数不 ...

  8. 2015年ACM长春区域赛比赛感悟

    距离长春区域赛结束已经4天了,是时候整理一下这次比赛的点点滴滴了. 也是在比赛前一周才得到通知要我参加长春区域赛,当时也是既兴奋又感到有很大的压力,毕竟我的第一场比赛就是区域赛水平,还是很有挑战性的. ...

  9. 2019~2020icpc亚洲区域赛徐州站H. Yuuki and a problem

    2019~2020icpc亚洲区域赛徐州站H. Yuuki and a problem 题意: 给定一个长度为\(n\)的序列,有两种操作: 1:单点修改. 2:查询区间\([L,R]\)范围内所有子 ...

随机推荐

  1. Dart 基本语法

    ?? // 如果b为null则赋值,否则保持原样 b ??= value; 级联符号.. querySelector('#confirm') // Get an object. ..text = 'C ...

  2. PHP加速器eAccelerator安装

    程序说明 eAccelerator是一个自由开放源码php加速器,优化和动态内容缓存,提高了php脚本的缓存性能,使得PHP脚本在编译的状态下,对 服务器的开销几乎为零. 它还有对脚本起优化作用,以加 ...

  3. Python中转换角度为弧度的radians()方法

    Python中转换角度为弧度的radians()方法 这篇文章主要介绍了Python中转换角度为弧度的radians()方法,是Python入门中的基础知识,需要的朋友可以参考下 radians()方 ...

  4. CSS元素隐藏

    { display: none; /* 不占据空间,无法点击 */ } /*************************************************************** ...

  5. Sqlserver实现故障转移 — 域控(1)

    一  .实现目的:实现两台sqlserver数据库服务器的实时备份及故障转移:即:其中一台数据库服务器宕机后,应用程序可自动连接到另一台数据库服务器继续运行. 二.域控:域控制器是指在“域”模式下,至 ...

  6. Kafka sender消息生产者

    1.pom文件引入Kafka依赖(我用的版本是2.2.2.RELEASE) <dependency> <groupId>org.springframework.kafka< ...

  7. JWT的实现原理

    前言最近在做一个python项目的改造,将python项目重构为java项目,过程中遇到了这个知识点,觉得这个蛮实用的,所以下班后回来趁热打铁写下这篇总结,希望后面的人能够有所借鉴,少走弯路. 一.优 ...

  8. 在kali2.0中使用msf图形界面可能会遇到的问题

    kali版本:Linux kali 4.9.0-kali3-amd64 #1 SMP Debian 4.9.18-1kali1 (2017-04-04) x86_64 GNU/Linux 编写日期:2 ...

  9. MARKDOWN使用文档

    ISSUE引用 引用当前项目内的ISSUE #1 markdown写法 #1 引用当前命名空间下的其他项目内的ISSUE projectname#1 sofa_ta#1 markdown写法 sofa ...

  10. noip2013day1-货车运输

    题目描述 \(A\)国有\(n\)座城市,编号从 \(1\)到\(n\),城市之间有 \(m\) 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 \(q\) 辆货车在运输货物, 司机们想知 ...