Codeforces Round #549
D. The Beatles(数论题)
【题目描述】
E.Lynyrd Skynyrd(好题)
【题目描述】
给定一个长度为n的模板序列,是1-n的某个排列;给定一个长度为m的序列a,现在有q次询问,每次询问序列a从li到ri是否存在一个非连续子串可以构成模板序列,是则输出1,否则输出0,答案为一串01序列
【思路】
区间查询,但查询的方式类似于字符串匹配,不像是线段树或者dp那样可以转移的东西,看了题解才知道居然是用倍增的方法来优化而已
倍增算法有好几个博客讲的很好
(https://blog.csdn.net/dong_qian/article/details/81702697)
遇到这种非连续的子串,好多都是记录一下模板串的下标,用pos记录一下,然后读入序列a的时候,在线处理一下当前这个数在模板串中的前一个数在a的最近一次出现位置,用lst来记录最近出现的那个位置,这是一个贪心的思想,从离得最近的那个蹦过来可以让完成匹配的区间尽可能小
然后用倍增数组处理一下蹦20,21,22…可以到达的位置,用f记录
再用另一个倍增数组G[i][j]记录序列a从i到i+2j这个区间所有数向左蹦n-1次可以到达的位置中最靠右的
然后处理每个查询的时候,把(l,r)区间一分为二(内部有重叠没关系),看看两个区间内G的值是否有未越界的
贴上蒟蒻代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
int n,m,q,a[200010],b[200010],pos[200010],lst[200010];
int f[200010][30],G[200010][30],L[200010];
int amax(int a,int b){
return a>b?a:b;
}
int main(){
freopen("E.in","r",stdin);
freopen("E.out","w",stdout);
int i,j,cnt,poss,t,l,r;
scanf("%d%d%d",&n,&m,&q);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for(i=1;i<=m;i++){
scanf("%d",&b[i]);
t=a[pos[b[i]]-1];
if(!t) t=a[n];
f[i][0]=lst[t];
lst[b[i]]=i;
}
for(i=1;1<<i<=n;i++){
for(j=1;j<=m;j++){
f[j][i]=f[f[j][i-1]][i-1];
}
}
for(i=1;i<=m;i++){
poss=i;
cnt=n-1;
for(j=20;j>=0;j--){
if(cnt-(1<<j)>=0){
cnt-=1<<j;
poss=f[poss][j];
}
}
G[i][0]=poss;
}
for(j=1;(1<<j)<=m;j++){
for(i=1;i+(1<<j)-1<=m;i++){
G[i][j]=amax(G[i][j-1],G[i+(1<<(j-1))][j-1]);
}
}
L[0]=-1;
for(i=1;i<=m;i++) L[i]=L[i>>1]+1;
for(i=1;i<=q;i++){
scanf("%d%d",&l,&r);
cnt=L[r-l+1];
if(amax(G[l][cnt],G[r-(1<<cnt)+1][cnt])>=l) printf("1");
else printf("0");
}
return 0;
}
F.U2
【题目描述】
给定n个坐标,两两之间可确定一个抛物线y=x^2+bx+c,找出最多组抛物线,使得剩余的点全都在抛物线外(下方)
【思路】
一开始想区间dp,不过n的规模显然是要爆内存的,然后突然觉得这不是很像凸包嘛(这么明显我都没看出来,还是看了cf的tag才发现,太菜 ),但是平方项真的太恶心了
原问题相当于y1=x12+b0x1+c0 y2=x22+b0x2+c0
若(x3,y3)在y=x2+bx+c的内部,则y3>x32+b0x3+c0
由于轨迹方程只和b,c有关,二次项系数始终为1,故将平方项移至左边
y3-x32>b0x3+c0
令y3’=y3-x32
即(x3,y3’)在(x1,y1’) (x2,y2’)的上方
故转化为凸包问题
这是一个向上凸的凸包,想象起始点在最左边那个点的y轴负方向无穷远处,故极角的排序转化为按x坐标升序,y坐标降序来排序
注意:由于是上凸,所以to_left判断为真(在左侧)时要出栈,在同一条线上时也要出栈,且x坐标相同时要去重
cf上有一个数据是n=1,所以特判一下
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
int s[200010],cnt,n,ans;
struct node{
long long x,y;
bool operator < (const node & tmp) const{
return x!=tmp.x?x<tmp.x:y>tmp.y;
}
}p[2000010];
bool to_left(node& a,node& b,node& c){
if((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x)<0) return false;
return true;
}
int main(){
freopen("F.in","r",stdin);
freopen("F.out","w",stdout);
int i;
scanf("%d",&n);
if(n==1){
printf("0\n");
return 0;
}
for(i=1;i<=n;i++){
scanf("%I64d%I64d",&p[i].x,&p[i].y);
p[i].y-=p[i].x*p[i].x;
}
sort(p+1,p+n+1);
s[++cnt]=1;
s[++cnt]=2;
for(i=3;i<=n;i++){
while(cnt>1&&to_left(p[s[cnt-1]],p[s[cnt]],p[i])){
cnt--;
}
s[++cnt]=i;
}
for(i=1;i<cnt;i++){
if(p[s[i]].x!=p[s[i+1]].x) ans++;
}
printf("%d\n",ans);
return 0;
}
Codeforces Round #548 div.2
D.Step to One
【题目描述】
【思路】
期望DP
正向推概率,反向推期望
概率DP/期望DP刷题总结
ACM概率期望DP总结
莫比乌斯反演入门讲解
这道题做了两个星期才A,因为没有接触过概率/期望DP,还在kuangbin上做了一些题,另外这题也是学习莫比乌斯反演的好题。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAX=100000;
const ll P=1000000007;
ll mo[MAX+5],dp[MAX+5],prime[MAX+5],m,ans;
int vis[MAX+5],cnt;
void get_mo(){//求莫比乌斯函数
memset(vis,0,sizeof(vis));
memset(mo,0,sizeof(mo));
mo[1]=1;
cnt=0;
for(int i=2;i<=MAX;i++){
if(!vis[i]){
prime[cnt++]=i;
mo[i]=-1;
}
for(int j=0;j<cnt&&prime[j]*i<=MAX;j++){
vis[prime[j]*i]=1;
if(i%prime[j]==0) break;
mo[prime[j]*i]=-mo[i];
}
}
}
ll pow(ll a,ll b,ll P){//求逆元
if(b==0) return 1;
ll cnt;
cnt=pow(a,b/2,P);
cnt=cnt*cnt%P;
if(b%2==1) cnt=cnt*a%P;
return cnt;
}
ll cal(ll x,ll mm){
ll kk=0;
for(int i=1;i*i<=x;i++){
if(x%i==0){
kk+=mo[i]*(mm/i)%P;
kk%=P;
if(x/i!=i){
kk+=mo[x/i]*(mm/(x/i))%P;
kk%=P;
}
}
}
return kk;
}
int main(){
freopen("DD.in","r",stdin);
freopen("DD.out","w",stdout);
get_mo();
scanf("%I64d",&m);
dp[1]=0;
for(int i=2;i<=m;i++){
dp[i]=m;
for(int j=2;j*j<=i;j++){
if(i%j==0){
dp[i]+=(cal(i/j,m/j)*dp[j]%P);
dp[i]%=P;
if(j!=i/j){
dp[i]+=(cal(j,m/(i/j))*dp[i/j]%P);
dp[i]%=P;
}
}
}
dp[i]=dp[i]*pow(m-m/i,P-2,P)%P;
dp[i]%=P;
}
ans=m;
for(int i=1;i<=m;i++)
ans=(ans+dp[i])%P;
ans=ans*pow(m,P-2,P)%P;
ans%=P;
printf("%I64d\n",ans);
return 0;
}
E.Maximize Mex
【题目描述】
有n个人,第i个人有pi潜力值,属于第ci个俱乐部,现在在d天之内,每天有一个人退出,每天,从每一个有人的俱乐部里选择一个人,他们的潜力值从0开始,找到最小的那个没有出现的自然数使它尽可能大
【思路】
二分图求mex是一种常用技巧 貌似?
二分图匹配的建图方法详见我的另一篇博客
本题采用的是行列式,建立pc的对应关系,只需要找到从0开始连续的最大可覆盖的值,而本题的另一个技巧是将删人改为加人,倒着加即可,因为二分图匹配没有删边的操作
dfs用来寻找增广路
下面附上代码
#include <bits/stdc++.h>
using namespace std;
const int MAX=5000;
vector edge[MAX+5];
int p[MAX+5],c[MAX+5],lft[MAX+5],used[MAX+5],vis[MAX+5],cp[MAX+5],ans[MAX+5];
int n,m,d,mex,dfn;
bool dfs(int x){
for(int i=0;i<edge[x].size();i++){
int j=edge[x][i];
if(used[j]!=dfn){
used[j]=dfn;
if(cp[j]==-1||dfs(cp[j])){
cp[j]=x;
return true;
}
}
}
return false;
}
int main(){
memset(cp,-1,sizeof(cp));
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&p[i]);
for(i=1;i<=n;i++)
scanf("%d",&c[i]);
scanf("%d",&d);
for(i=1;i<=d;i++){
scanf("%d",&lft[i]);
vis[lft[i]]=1;
}
for(i=1;i<=n;i++){
if(!vis[i]) edge[p[i]].push_back(c[i]);
}
for(i=d,mex=-1,dfn=1;i>=1;i–){
while(dfs(mex+1)){mex++;dfn++;}
ans[i]=mex+1;
dfn++;
edge[p[lft[i]]].push_back(c[lft[i]]);
}
for(i=1;i<=d;i++){
printf("%d\n",ans[i]);
}
return 0;
}
Codeforces Round #547 div.3
F2.Same Sum Blocks (Hard)
【题目描述】
题目描述
有n个数,求出不相交的连续子串和相同的个数最多的区间
【思路】
这题玩的一手好stl
先贴上几个关于STL的博客
map的用法
下标访问负数的方法
//map的下标类型真的是开了bug一样
二维map
//map和vector的搭配真的是绝了
pair的用法
set的用法详解
auto的用法详解
//不过auto好像在dev上总是编译不了不知道为什么
G.Privatization of Roads in Treeland
【题目描述】
题目链接
有一个有n个结点的树(即n-1条边),用最少的染料给树的边染色,连向同一个结点的边染重复颜色则为“bad”树,bad树最多不超过k个,输出最少染料数以及每条边染的颜色
【思路】
从n范围看出这题大概是nlgn的算法吧,统计每个节点的度,坏树一定是度最多的那几个,然后二分一下答案。最后用bfs苒一下色,bfs的部分我开了一个ab数组,也就是向外连接的当前起始点与它的父节点连边的染色,然后从这个颜色开始循环
不过很多题解用dfs做,用当前点当前连边数+1作为染色值赋给新加的边
一开始脑子抽了搞了个拓扑排序,后来发现这个是无向图啊,而且就算每次全部判断一下也不会耗费多长时间,果然是div3的难度
#include <bits/stdc++.h>
using namespace std;
const int MAX=200000;
vector<int> edge[MAX+5];
vector<int> ans;
vector<pair<int,int> >od;
map<pair<int,int>,int> mp;
pair<int,int> p;
queue<int> s;
int ansn,amax,n,k,in[MAX+5],vis[MAX+5],ab[MAX+5],cnt;
/*void topo(){
int i,t;
for(i=1;i<=n;i++){
if(in[i]==1) s.push(i);
}
while(!s.empty()){
t=s.front();
vis[t]=1;
s.pop();
ans.push_back(t);
for(i=0;i<edge[t].size();i++){
if(vis[edge[t][i]]) continue;
in[edge[t][i]]--;
if(in[edge[t][i]]==1) s.push(edge[t][i]);
}
}
}*/
bool pd(int m){
int i,num=0;
for(i=1;i<=n;i++){
if(in[i]>m) num++;
}
if(num>k) return true;
return false;
}
int bi_search(){
int ll,rr,mm;
ll=1;
rr=amax;
while(ll<rr){
mm=(ll+rr)/2;
if(!pd(mm)){
rr=mm;
}
else{
ll=mm+1;
}
}
return rr;
}
void bfs(int st){
memset(vis,0,sizeof(vis));
while(!s.empty()){
s.pop();
}
s.push(st);
vis[st]=1;
int u,v,i;
while(!s.empty()){
u=s.front();
s.pop();
cnt=ab[u];
for(i=0;i<edge[u].size();i++){
v=edge[u][i];
if(!vis[v]){
vis[v]=1;
cnt++;
if(cnt>ansn) cnt=1;
p.first=u;
p.second=v;
mp[p]=cnt;
p.first=v;
p.second=u;
mp[p]=cnt;
ab[v]=cnt;
s.push(v);
}
}
}
}
void print(){
printf("%d\n",ansn);
for(int i=0;i<od.size();i++){
printf("%d ",mp[od[i]]);
}
}
int main(){
freopen("G.in","r",stdin);
freopen("G.out","w",stdout);
int i,u,v;
scanf("%d%d",&n,&k);
for(i=1;i<n;i++){
scanf("%d%d",&u,&v);
od.push_back(make_pair(u,v));
edge[u].push_back(v);
edge[v].push_back(u);
}
for(i=1;i<=n;i++){
in[i]=edge[i].size();
if(in[i]>amax) amax=in[i];
}
//topo();
ansn=bi_search();
bfs(1);
print();
return 0;
}