杂题选录
LuoguP3948数据结构 10-20
是比较裸的差分题目,但是要注意在线查询的时候开始傻了,每次都暴力地从1到n搞一遍,还存在数组中每次都要清空...结果T了很多点。
其实在线查询的时候直接用变量+扫到r就行了。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 5 using namespace std; 6 typedef long long ll; 7 8 int n,opt,tot,fin; 9 char doit[5]; 10 ll sjt,minn,maxx; 11 ll cha[100000],sta[100000],sum[100000]; 12 13 int main() 14 { 15 scanf("%d%d",&n,&opt); 16 scanf("%lld%lld%lld",&sjt,&minn,&maxx); 17 for(int i=1;i<=opt;i++) 18 { 19 int l=0,r=0;ll x=0; 20 scanf("%s",doit+1); 21 if(doit[1]=='A') 22 { 23 scanf("%d%d%lld",&l,&r,&x); 24 cha[l]+=x,cha[r+1]-=x; 25 } 26 else 27 { 28 scanf("%d%d",&l,&r); 29 ll noww=0,cnt=0; 30 for(int i=1;i<=r;i++) 31 { 32 noww+=cha[i]; 33 ll cmp=noww*i%sjt; 34 if(i>=l&&cmp>=minn&&cmp<=maxx) cnt++; 35 } 36 printf("%lld\n",cnt); 37 } 38 } 39 for(int i=1;i<=n;i++) sta[i]=sta[i-1]+cha[i]; 40 for(int i=1;i<=n;i++) 41 { 42 sum[i]=sum[i-1]; 43 ll cmp=sta[i]*i%sjt; 44 if(cmp>=minn&&cmp<=maxx) sum[i]++; 45 } 46 scanf("%d",&fin); 47 for(int i=1;i<=fin;i++) 48 { 49 int l=0,r=0; 50 scanf("%d%d",&l,&r); 51 printf("%lld\n",sum[r]-sum[l-1]); 52 } 53 return 0; 54 }
sjtu1329 聚餐 10-20
二进制枚举裸题。从数据范围中就可以看出,结果卡了半个小时多...因为单词拼错了....sigh拼成sign...我真傻,真的。
另外注意因为二进制数位从0开始记起,所以要对于菜肴号要减1.
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 5 using namespace std; 6 7 int T,n,m,fake,ans; 8 char sss; 9 int request[100][100],num[100]; 10 11 inline int read(){ 12 char ch=getchar();int x=0,f=1; 13 while(ch<'0' || ch>'9') { 14 if(ch=='-') f=-1; 15 ch=getchar(); 16 } 17 while(ch<='9' && ch>='0') { 18 x=x*10+ch-'0'; 19 ch=getchar(); 20 } 21 sss=ch; 22 return x*f; 23 } 24 25 int main() 26 { 27 scanf("%d",&T); 28 while(T--) 29 { 30 scanf("%d%d",&n,&m); 31 for(int i=1;i<=m;i++) 32 for(int j=1;j<=n;j++) 33 { 34 num[i]++; 35 request[i][j]=read(); 36 if(sss!=' ') break; 37 } 38 fake=(1<<n)-1; 39 for(int i=0;i<=fake;i++) 40 { 41 int cnt=0; 42 for(int j=1;j<=m;j++) 43 for(int k=1;k<=num[j];k++) 44 { 45 if(request[j][k]<0) 46 { 47 int tmp=-request[j][k]-1; 48 if(!((i>>tmp)&1)) {cnt++;break;} 49 } 50 else 51 { 52 int tmp=request[j][k]-1; 53 if((i>>tmp)&1) {cnt++;break;} 54 } 55 } 56 ans=max(ans,cnt); 57 } 58 if(ans==m) printf("Bingo!\n"); 59 else printf("Sigh...\n"); 60 ans=0; 61 memset(num,0,sizeof(num)); 62 } 63 return 0; 64 }
chengni邀请赛 我宋金涛天下第一 10-21
看到这个函数的启发,我首先想的是前几天正睿的分层图最短路,三次迭代之后会回到原来的值。于是手推了下几个随机的小奇数/偶数。发现最后都会变成1。于是就又想到线段树维护区间开方那题,可能这道题也是不是连续搞几次就过了呢...然后就写了...然后自己出了数据开始对拍大样例。发现锅了...发现修改的时候写成区间了,写成单点即可。
不过当然是托了数据随机的福,不然会T死啊
1 #include<cstdio> 2 #include<algorithm> 3 #define maxn 200090 4 5 using namespace std; 6 typedef long long ll; 7 8 int n,m; 9 ll seq[maxn]; 10 struct SegmentTree{ 11 int l,r; 12 ll sum,val; 13 }t[maxn*4]; 14 15 ll f(ll x) 16 { 17 if(x==1) return 1; 18 if(x&1) return 3*x+1; 19 return x>>1; 20 } 21 22 void build(int p,int l,int r) 23 { 24 t[p].l=l,t[p].r=r; 25 if(l==r) 26 { 27 t[p].val=t[p].sum=seq[l]; 28 return ; 29 } 30 int mid=(l+r)>>1; 31 build(p*2,l,mid); 32 build(p*2+1,mid+1,r); 33 t[p].sum=t[p*2].sum+t[p*2+1].sum; 34 t[p].val=max(t[p*2].val,t[p*2+1].val); 35 } 36 37 void change(int p,int l,int r) 38 { 39 if(t[p].l==t[p].r) 40 { 41 t[p].val=f(t[p].val); 42 t[p].sum=f(t[p].sum); 43 return ; 44 } 45 int mid=(t[p].l+t[p].r)>>1; 46 if(l<=mid&&t[p*2].val>1) change(p*2,l,r); 47 if(r>mid&&t[p*2+1].val>1) change(p*2+1,l,r); 48 t[p].sum=t[p*2].sum+t[p*2+1].sum; 49 t[p].val=max(t[p*2].val,t[p*2+1].val); 50 } 51 52 ll ask(int p,int l,int r) 53 { 54 if(t[p].l==l&&t[p].r==r) return t[p].sum; 55 int mid=(t[p].l+t[p].r)>>1; 56 if(l>mid) return ask(p*2+1,l,r); 57 else if(r<=mid) return ask(p*2,l,r); 58 else return ask(p*2,l,mid)+ask(p*2+1,mid+1,r); 59 } 60 61 int main() 62 { 63 64 scanf("%d%d",&n,&m); 65 for(int i=1;i<=n;i++) scanf("%lld",&seq[i]); 66 build(1,1,n); 67 while(m--) 68 { 69 int op=0,l=0,r=0; 70 scanf("%d%d%d",&op,&l,&r); 71 if(op==1) change(1,l,r); 72 else if(op==2) printf("%lld\n",ask(1,l,r)); 73 } 74 return 0; 75 }
chengni邀请赛 我再也不划水了 10-21
模拟题啊QAQ。注意AKNOI/AKIOI等卡牌满足大于等于即可。而不苛刻地等于。
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 5 using namespace std; 6 7 int T; 8 double ans; 9 int tong[1000]; 10 char op[10]; 11 12 void add() 13 { 14 if(tong[0]>=1&&tong[10]>=1&&tong[8]>=2&&tong[14]>=1) {ans+=100;return ;} 15 if(tong[0]==5||tong[10]==5||tong[13]==5||tong[14]==5||tong[8]==5||tong[15]==5) 16 {ans+=95;return ;} 17 if(tong[0]>=1&&tong[10]>=1&&tong[13]>=1&&tong[14]>=1&&tong[8]>=1) {ans+=90;return ;} 18 if(tong[8]>=2&&tong[14]>=1) {ans+=85;return ;} 19 if(tong[8]>=1&&tong[13]>=1&&tong[14]>=1) {ans+=80;return ;} 20 if(tong[13]>=1&&tong[14]>=1&&tong[8]>=1) {ans+=75;return ;} 21 if(tong[0]>=1&&tong[10]>=1) {ans+=70;return ;} 22 if(tong[0]==4||tong[10]==4||tong[13]==4||tong[14]==4||tong[8]==4||tong[15]==4) 23 {ans+=60;return ;} 24 if(tong[0]==3||tong[10]==3||tong[13]==3||tong[14]==3||tong[8]==3||tong[15]==3) 25 {ans+=50;return ;} 26 int pos=1,tmp=0; 27 for(int i=1;i<=tong[0];i++) tmp+=pos*1,pos++; 28 for(int i=1;i<=tong[8];i++) tmp+=pos*4,pos++; 29 for(int i=1;i<=tong[10];i++) tmp+=pos*7,pos++; 30 for(int i=1;i<=tong[13];i++) tmp+=pos*11,pos++; 31 for(int i=1;i<=tong[14];i++) tmp+=pos*12,pos++; 32 for(int i=1;i<=tong[15];i++) tmp+=pos*19,pos++; 33 tmp%=27; 34 ans+=tmp; 35 } 36 37 void A() 38 { 39 tong['A'-'A']++;add();tong['A'-'A']--; 40 } 41 42 void K() 43 { 44 tong['K'-'A']++;add();tong['K'-'A']--; 45 } 46 47 void N() 48 { 49 tong['N'-'A']++;add();tong['N'-'A']--; 50 } 51 52 void O() 53 { 54 tong['O'-'A']++;add();tong['O'-'A']--; 55 } 56 57 void I() 58 { 59 tong['I'-'A']++;add();tong['I'-'A']--; 60 } 61 62 void P() 63 { 64 tong['P'-'A']++;add();tong['P'-'A']--; 65 } 66 67 int main() 68 { 69 scanf("%d",&T); 70 while(T--) 71 { 72 scanf("%s",op+1); 73 for(int i=1;i<=4;i++) 74 tong[op[i]-'A']++; 75 A(); 76 K(); 77 N(); 78 O(); 79 I(); 80 P(); 81 ans=ans/6; 82 printf("%.3lf\n",ans); 83 ans=0; 84 tong[0]=0,tong[10]=0,tong[13]=0,tong[14]=0,tong[8]=0,tong[15]=0; 85 } 86 return 0; 87 }
CF484A Bits 10-22
题目大意:求区间$[l,r]$中,二进制表示中1个数最多的区间内的数。
贪心地从$l$开始一位位填,但是开始想的比较naive,先统计下$l$的位数,再把位数上所有的数都填成1.注意,这里都填成1后可能就会超过$r$了,这个贪心是错的。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 typedef long long ll; 6 7 int T; 8 ll l,r; 9 10 ll ksm(ll a,ll b) 11 { 12 ll tmp=1; 13 while(b) 14 { 15 if(b&1) tmp=tmp*a; 16 b>>=1; 17 a=a*a; 18 } 19 return tmp; 20 } 21 22 int work(ll x) 23 { 24 int tot=0; 25 ll tmp=x; 26 while(tmp) 27 { 28 tmp/=2; 29 tot++; 30 } 31 return tot; 32 } 33 34 int main() 35 { 36 scanf("%d",&T); 37 while(T--) 38 { 39 scanf("%lld%lld",&l,&r); 40 if(l==r){printf("%lld\n",l);continue;} 41 int cnt=work(l); 42 ll begi=(1<<cnt)-1; 43 while(1) 44 { 45 if(begi+ksm(2,cnt)>r) break; 46 begi+=ksm(2,cnt); 47 cnt++; 48 } 49 printf("%lld\n",begi); 50 } 51 return 0; 52 }
但是还好整体方向是对的,即每一位从$l$开始尽量填1。这里我们可以用位运算(或运算)来帮忙。但是注意由于位运算优先级很低,所以我们需要大力加括号。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 typedef long long ll; 6 7 int T; 8 ll l,r; 9 10 int main() 11 { 12 scanf("%d",&T); 13 while(T--) 14 { 15 scanf("%lld%lld",&l,&r); 16 if(l==r){printf("%lld\n",l);continue;} 17 for(ll i=1;(i|l)<=r;i<<=1) l|=i; 18 printf("%lld\n",l); 19 } 20 return 0; 21 }
sjtu 1621 未命名 10-21
题目大意:给你一个01矩阵,你可以任意交换两行无限多次,求最大全白子矩形大小。
这个思想在之前做的一道题中体现过。(玉蟾宫)。
一众Zhengrui学成而归的dalao表示这是他们做过一道类似的原题。只不过他们是可以交换任意两列无限多次。不过我们把地图翻转过来不就一样了嘛qwq。
思路:设$f[i][j]$表示从$(i,j)$向上最多能延伸的高度(都是1的部分)。然后我们枚举每一行对同一行上的$f[i][j]$进行从小到大排序。(即任意交换两列),然后尝试向右向上扩展。因为是从小到大排序的,所以对于同一行上的$i<j$,$i$位置能延伸到的,$j$位置同样能延伸到。就可以从这里出发更新答案。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 6 int n,ans; 7 int f[1090][1090]; 8 char tmp[1090],last[1090][1090],mapp[1090][1090]; 9 10 int main() 11 { 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++) 14 { 15 scanf("%s",tmp+1); 16 for(int j=1;j<=n;j++) last[i][j]=tmp[j]; 17 } 18 for(int i=1;i<=n;i++) 19 { 20 for(int j=n;j>=1;j--) 21 mapp[j][i]=last[i][n-j+1]; 22 } 23 for(int i=1;i<=n;i++) 24 for(int j=1;j<=n;j++) 25 if(mapp[i][j]=='1') f[i][j]=f[i-1][j]+1; 26 for(int i=1;i<=n;i++) 27 { 28 sort(f[i]+1,f[i]+1+n); 29 for(int j=1;j<=n;j++) 30 ans=max(ans,f[i][j]*(n-j+1)); 31 } 32 printf("%d",ans); 33 return 0; 34 }
chengni给对面新生出的题 树链剖分
题目大意:求出满足树链剖分要求的字典序最小的$dfs$序。
思路:因为我们要满足树链剖分的性质,所以肯定先递归重儿子这是毋庸置疑的。那么字典序的大小差异只会产生在轻儿子中。表示被邻接表限制了想象力,我们可以用$vector$存边。然后对每个点的儿子vector集合排个序就好了。
1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 #define maxn 10090 5 6 using namespace std; 7 8 int n,tot,cnt; 9 int size[maxn],rk[maxn],son[maxn]; 10 vector<int>s[maxn]; 11 12 void dfs1(int u,int fa) 13 { 14 size[u]=1; 15 for(int i=0;i<s[u].size();i++) 16 { 17 int v=s[u][i]; 18 if(v==fa) continue; 19 dfs1(v,u); 20 size[u]+=size[v]; 21 if(size[v]>size[son[u]]) son[u]=v; 22 } 23 } 24 25 void dfs2(int u,int fa) 26 { 27 rk[++cnt]=u; 28 if(!son[u]) return ; 29 dfs2(son[u],u); 30 for(int i=0;i<s[u].size();i++) 31 { 32 int v=s[u][i]; 33 if(fa==v||v==son[u]) continue; 34 dfs2(v,u); 35 } 36 } 37 38 int main() 39 { 40 scanf("%d",&n); 41 for(int i=1;i<=n-1;i++) 42 { 43 int x=0,y=0; 44 scanf("%d%d",&x,&y); 45 s[x].push_back(y); 46 s[y].push_back(x); 47 } 48 for(int i=1;i<=n;i++) sort(s[i].begin(),s[i].end()); 49 dfs1(1,0); 50 dfs2(1,0); 51 for(int i=1;i<=cnt;i++) printf("%d ",rk[i]); 52 return 0; 53 }
chengni给对面新生出的题 线段树
题目大意:给你一个线段树和很多区间,求这个区间被多少个区间恰好表示。
思路:吸取线段树中的思路:递归求解即可。但是写的有点慢啊...我还是太菜了。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 6 int n,m; 7 8 int ask(int l,int r,int posl,int posr) 9 { 10 int mid=(l+r)>>1; 11 if(l>=posl&&r<=posr) return 1; 12 if(posl>mid) return ask(mid+1,r,posl,posr); 13 else if(posr<=mid) return ask(l,mid,posl,posr); 14 else return ask(l,mid,posl,posr)+ask(mid+1,r,posl,posr); 15 } 16 17 int main() 18 { 19 scanf("%d%d",&n,&m); 20 for(int i=1;i<=m;i++) 21 { 22 int l=0,r=0; 23 scanf("%d%d",&l,&r); 24 printf("%d\n",ask(1,n,l,r)); 25 } 26 return 0; 27 }
2018-10-21noi.ac邀请赛Day2 ball
数轴上有 n 个球,每个球直径为 1,第 i 个球的左端点为 pi(即占据了数轴上 [pi,pi+1]。在 P位置有一堵墙。
有q个操作,每次要么以x位置为左端点放一个新球(如果有了就不管),要么把最左边的球往右推。一个球碰到另一个的时候,旧球停下来,新球继续滚。球碰到墙的时候就停下来。
最后你需要输出所有球的位置。
手玩下样例可以发现,每次进行操作2后,每个球的位置会变成它之后的那个球的位置减1,于是便有了一个$O(n^2)$算法,每次都$O(n)$地更新一下序列,并用一个$vis$数组记录下这个位置有没有被占用。加元素的时候重新排遍序就行了。期望得分30分。
1 #include<cstdio> 2 #include<algorithm> 3 #include<map> 4 5 using namespace std; 6 7 int n,Q,wal,tot; 8 int p[200090]; 9 map<int,int>vis; 10 11 int main() 12 { 13 scanf("%d%d%d",&n,&Q,&wal); 14 for(int i=1;i<=n;i++) scanf("%d",&p[i]),vis[p[i]]=1; 15 p[n+1]=wal;tot=n+1; 16 sort(p+1,p+1+n+1); 17 if((n<=1000&&Q<=1000)||Q<n) 18 { 19 while(Q--) 20 { 21 int op=0,x=0; 22 scanf("%d",&op); 23 if(op==1) 24 { 25 scanf("%d",&x); 26 if(vis[x]) continue; 27 p[++tot]=x; 28 sort(p+1,p+tot+1); 29 vis[x]=1; 30 } 31 else if(op==2) 32 { 33 for(int i=1;i<=tot-1;i++) 34 vis[p[i]]=0,p[i]=p[i+1]-1,vis[p[i]]=1; 35 } 36 } 37 for(int i=1;i<=tot-2;i++) printf("%d ",p[i]); 38 printf("%d",p[tot-1]); 39 return 0; 40 } 41 if(Q>=n) 42 { 43 int bg=wal-n; 44 for(int i=bg;i<=wal-2;i++) printf("%d ",i); 45 printf("%d",wal-1); 46 return 0; 47 } 48 return 0; 49 }
其实一定快要想到正解了,但是没有往深处想:考虑操作2,球的数量是一定的,只是位置发生变化,于是每次推动的时候,是将每个球的位置-1,再把最左边的球放到$p-1$处。因此我们只需记-1的次数,再用set维护。set是可以一个元素不可重复、自动排序(类似优先队列)的集合,插入函数insert,如果元素已经存在则操作不进行;还有强大的erase操作。某些程度上可以代替迭代器。输出元素时要加*号解除引用。
1 #include<cstdio> 2 #include<algorithm> 3 #include<set> 4 5 using namespace std; 6 7 int n,q,p,k,x; 8 set<int>S; 9 10 int main() 11 { 12 scanf("%d%d%d",&n,&q,&p); 13 for(int i=1;i<=n;i++) scanf("%d",&x),S.insert(x); 14 while(q--) 15 { 16 int op=0; 17 scanf("%d",&op); 18 if(op==1) scanf("%d",&x),S.insert(k+x); 19 else S.erase(S.begin()),S.insert(p+k),k++; 20 } 21 while(S.size()) 22 printf("%d ",*S.begin()-k),S.erase(S.begin()); 23 return 0; 24 }
ZROI Day14 字符串
题目大意:给定一个长度为$n$的字符串,它的字典集为$M$。也就是说每一位有$M$种不同的字母可以选。对于这个字符串所有连续的长度为$k$的子串都必须是回文串,求有多少不同方案。
其实是道数学题,我们分情况讨论。
当$k>n$或$k=1$时,显然答案为$m^n$。因为这是不再有回文串的限制。
当$k=n$时,不难想,设$qwq$=$(n+1)>>1$,那么答案为$m^{qwq}$。因为这时配对有$(n+1)>>1$组。这启发我们统计可能的对数。
当$k<n$时,我们可以多画一下图,合并一定相等的位置。最后总结出当$k$为奇数,答案为$m^2$,因为只能有两对。当$k$为偶数,答案为$m$。因为所有字符都必须一样。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 typedef long long ll; 6 ll moder=1e9+7; 7 8 ll n,m,k; 9 10 ll ksm(ll a,ll b) 11 { 12 ll tmp=1; 13 while(b) 14 { 15 if(b&1) tmp=tmp*a%moder; 16 b>>=1; 17 a=a*a%moder; 18 } 19 return tmp; 20 } 21 22 int main() 23 { 24 scanf("%lld%lld%lld",&n,&m,&k); 25 if(k>n||k==1) {printf("%lld\n",ksm(m,n)%moder);return 0;} 26 if(k==n) {printf("%lld\n",ksm(m,(n+1)>>1)%moder);return 0;} 27 if(k&1) printf("%lld\n",ksm(m,2)); 28 else printf("%lld\n",m); 29 return 0; 30 }
牛客某比赛 小可爱序列
题目大意:
a.将最后一个数挪到第一位
b.将序列第3位挪到第一位
你需要给出最后的序列。链接:https://ac.nowcoder.com/acm/contest/224/B
对于所有数据
4<=n<=2000
m<=100000
对于每次操作的操作数<=998244353
又想到了那道《我宋全涛天下第一》,感觉这么大的操作数显然就是在唬人:对于a操作,只要操作膜3以后的次数即可;对于b操作同理,膜$n$以后的次数即可。具体实现我们两个双端队列即可。
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 5 using namespace std; 6 7 int n,m,x,num; 8 deque<int>q1,q2; 9 int tmp[10]; 10 char op[10]; 11 12 void work1() 13 { 14 num%=3; 15 //printf("%d\n",num); 16 for(int i=1;i<=num;i++) 17 x=q1.back(),q1.pop_back(),q1.push_front(x); 18 for(int i=1;i<=3;i++) 19 tmp[i]=q1.front(),q1.pop_front(),q2.pop_front(); 20 for(int i=3;i>=1;i--) 21 q1.push_front(tmp[i]),q2.push_front(tmp[i]); 22 } 23 24 void work2() 25 { 26 num%=n; 27 //printf("%d\n",num); 28 for(int i=1;i<=num;i++) 29 x=q2.back(),q2.pop_back(),q2.push_front(x); 30 for(int i=1;i<=3;i++) 31 tmp[i]=q2.front(),q2.pop_front(); 32 q1.clear(); 33 for(int i=3;i>=1;i--) q1.push_front(tmp[i]),q2.push_front(tmp[i]); 34 } 35 36 int main() 37 { 38 scanf("%d%d",&n,&m); 39 for(int i=1;i<=3;i++) 40 scanf("%d",&x),q1.push_back(x),q2.push_back(x); 41 for(int i=4;i<=n;i++) 42 scanf("%d",&x),q2.push_back(x); 43 for(int i=1;i<=m;i++) 44 { 45 scanf("%d",&num); 46 scanf("%s",op+1); 47 if(op[1]=='a') work2(); 48 else if(op[1]=='b') work1(); 49 // for(int j=1;j<=n;j++) 50 // x=q2.front(),printf("%d ",x),q2.pop_front(),q2.push_back(x); 51 // printf("\n"); 52 } 53 for(int i=1;i<=n;i++) 54 x=q2.front(),q2.pop_front(),printf("%d ",x); 55 return 0; 56 }
LuoguP2338失败的滑雪
其实是道大模拟==。但是码力不足需要练==。而且需要有一个清晰的思路,不能以距离为轴,因为每次走一秒加上速度的话,中间的时间可能还有失误,这是看不出来的:(错误代码)
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 6 int n,v=1,tot; 7 char op[10]; 8 bool tim[10000090],dis[2000]; 9 10 int main() 11 { 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++) 14 { 15 int x=0; 16 scanf("%s",op+1); 17 scanf("%d",&x); 18 if(op[1]=='T') tim[x]=1; 19 else if(op[1]=='D') dis[x]=1; 20 } 21 for(int i=1;i<=1000;i++) 22 { 23 tot+=v; 24 if(dis[i]) v++; 25 if(tim[tot]) v++; 26 } 27 printf("%d\n",tot); 28 return 0; 29 }
正确做法:以失误为轴
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 6 int n,cntt,cntd; 7 double pos,v=1,tot,tv; 8 char op[10]; 9 int tim[20000],dis[20000]; 10 11 int main() 12 { 13 scanf("%d",&n); 14 for(int i=1;i<=n;i++) 15 { 16 int x=0; 17 scanf("%s",op+1); 18 scanf("%d",&x); 19 if(op[1]=='T') tim[++cntt]=x; 20 else if(op[1]=='D') dis[++cntd]=x; 21 } 22 sort(tim+1,tim+1+cntt);tim[cntt+1]=1e9; 23 sort(dis+1,dis+1+cntd);dis[cntd+1]=1e9; 24 int i=1,j=1; 25 while(i<=cntt||j<=cntd) 26 { 27 tv=1.0/v; 28 double pos1=pos+tv*(tim[i]-tot); 29 double pos2=dis[j]; 30 if(pos1<pos2) 31 { 32 pos=pos1; 33 tot=tim[i]; 34 i++;v++; 35 } 36 else 37 { 38 tot+=(pos2-pos)/tv; 39 pos=pos2; 40 j++;v++; 41 } 42 } 43 tv=1.0/v; 44 tot+=(1000-pos)/tv; 45 printf("%.0lf",tot); 46 return 0; 47 }
以及四舍五入(double)的输出方式:”.0lf“。涨姿势了/cy。
ZROI day16 道路规划
题目大意:给你一个$n$个点的无向正权连通图,以及一个记录两点间最短路的矩阵,请问至少需要多少条边能满足这个矩阵。$n<=300$。
两个点的最短路相连方式有两种:通过其他点松弛来的/两个点直接相连。
考虑$floyd$算法,若$f[i][j]=f[i][k]+f[k][j]$,那么说明图中存在其他点使$i$与$j$有最短路,如果不能松弛,那么说明需要另在两个点间连边。计数就是这样计出来的。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 6 int n,ans; 7 int f[500][500],vis[500][500]; 8 9 int main() 10 { 11 scanf("%d",&n); 12 for(int i=1;i<=n;i++) 13 for(int j=1;j<=n;j++) 14 scanf("%d",&f[i][j]); 15 for(int k=1;k<=n;k++) 16 for(int i=1;i<=n;i++) 17 for(int j=1;j<=n;j++) 18 { 19 if(i==k||j==k||i==j) continue; 20 if(f[i][j]==f[i][k]+f[k][j]) vis[i][j]=1; 21 } 22 for(int i=1;i<=n;i++) 23 for(int j=i+1;j<=n;j++) 24 if(!vis[i][j]) ans++; 25 printf("%d\n",ans); 26 return 0; 27 }
在正睿参加的最后一场模拟赛 Standard T1 Problem
开始给$f$函数打的表,企图发现一些规律,但是打了很久没看出来...后来手动搞了一个数试验一下,发现是各数位的平方和。例如$f(531)=5^2+3^2+1^2$。
打表的时候发现其实能满足条件的数其实很少...不妨枚举$k$的倍数,那么计算的范围就会少很多。实际上最多只需要算18*81(9*9)。这是1e18的最大$f$值 。枚举的时候注意判断边界,以及列出的式子是否正确。(错了很多次)还有计算$f$函数的时候,其实正常算就好,考场上又晕了。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 typedef long long ll; 6 7 int T; 8 ll k,l,r,ans; 9 10 ll f(ll x) 11 { 12 ll F=0,tmp=x; 13 while(tmp) 14 { 15 F+=(tmp%10)*(tmp%10); 16 tmp/=10; 17 } 18 return F; 19 } 20 21 int main() 22 { 23 scanf("%d",&T); 24 while(T--) 25 { 26 scanf("%lld%lld%lld",&k,&l,&r); 27 for(ll i=0;i<=81*18;i++) 28 { 29 if(i*k<l) continue; 30 if(i*k>r) break; 31 if(f(i*k)*k==i*k) ans++; 32 } 33 printf("%lld\n",ans); 34 ans=0; 35 } 36 return 0; 37 }
Permutation Problem
如何拿40分的n<=17情况。当然是状压!!但是考场上只把全排列的部分分拿了。
可以暴力dp,设$f[i]$表示放了的集合为$i$(即放了为1没放为0)时的方案数,可以做到$O(n*2^n)$转移。转移方程$f[i|(1<<j)]$+=$f[i]$。还要注意初值$f[0]=1$。
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 typedef long long ll; 6 ll moder=1e9+7; 7 8 int n,k,fake; 9 ll f[140000]; 10 11 int main() 12 { 13 scanf("%d%d",&n,&k); 14 if(k==0) {printf("1");return 0;} 15 fake=(1<<n)-1; 16 f[0]=1; 17 for(int i=0;i<=fake;i++) 18 { 19 int pos=1; 20 for(int j=0;j<n;j++) 21 if(i&(1<<j)) pos++; 22 for(int j=0;j<n;j++) 23 { 24 if(i&(1<<j)) continue; 25 if(abs(j+1-pos)>k) continue; 26 f[i|(1<<j)]+=f[i]; 27 f[i|(1<<j)]%=moder; 28 } 29 } 30 printf("%lld",f[fake]); 31 return 0; 32 }
其实用状压搞部分分的情况也是不少。比如某次noi.ac测试,也出了一道类似排列的题,我们同样能用状压搞到不错的分数。原题是这样的