题目链接

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. Jmeter(十三) JDBC Request

    Jmeter中取样器(Sampler)是与服务器进行交互的单元.一个取样器通常进行三部分的工作:向服务器发送请求,记录服务器的响应数据和记录响应时间信息 有时候工作中我们需要对数据库发起请求或者对数据 ...

  2. 微信小程序的生命周期和APP对象的使用

    1.生命周期和APP对象的使用: //app.js App({ onLaunch: function () { //调用API从本地缓存中获取数据 var logs = wx.getStorageSy ...

  3. 2019JAVA第十次实验报告

    Java实验报告 班级 计科二班 学号 20188442 姓名 吴怡君 完成时间 2019.11.15 评分等级 实验代码 package Domon9; import java.awt.Font; ...

  4. windows如何使用bat快速安装计划任务?

    关键词:windows定时任务,schtasks,at ,bat schtasks 部分转自: https://www.cnblogs.com/yumianhu/p/3710743.html at的详 ...

  5. (5.1.5)引擎管理——多服务器管理之中央管理服务器(CMS)

    关键词:中央管理服务器,CMS,多服务器管理 中央管理服务器 -[1]打开 视图->已注册的服务器 [2]注册中央管理服务器 右击中央管理器->注册中央管理服务器 这里输入IP.主机名都可 ...

  6. 思考--mysql 分库分表的思考

    查询不在分库键上怎么办,扫描所有库?由于分库了,每个库扫描很快?所以比单个表的扫描肯定快,可以这样理解吗. 多表jion怎么弄,把内层表发给每个分库吗? citus,tidb 都有这些问题,citus ...

  7. 小记---------linux远程连接集群内其他机器mysql库

    mysql -h -u maxwell -p#10.0.15.145 远程机器ip#-P 注意是大写P 端口#-u 用户#-p 密码

  8. .Net Core IIS下无Log4Net日志输出,命令行下却有(dotnet运行)

    .Net Core IIS下无Log4Net日志输出,命令行下却有(dotnet运行) 遇到个诡异的问题,项目发布并寄宿到 IIS上后,Log4Net没有日志输出 1.原因分析 这不应该啊,所有的配置 ...

  9. sql中循环的存储过程

    ), a2 bigint, a3 bigint) returns void as $$declare ii integer; begin II:; FOR ii IN a2..a3 LOOP INSE ...

  10. Matcher和Pattern总结

    Matcher.Pattern简介 java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.它包括两个类:Pattern和Matcher Pattern 一个Pat ...