杂题选录

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
",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
",sum[r]-sum[l-1]);
52     }
53     return 0;
54 }
View Code

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!
");
59         else printf("Sigh...
");
60         ans=0;
61         memset(num,0,sizeof(num));
62     }
63     return 0;
64 }
View Code

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
",ask(1,l,r));
73     }
74     return 0;
75 }
View Code

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
",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 }
View Code

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
",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
",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
",l);continue;}
17         for(ll i=1;(i|l)<=r;i<<=1) l|=i; 
18         printf("%lld
",l);
19     }
20     return 0;
21 }
View Code

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 }
View Code

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 }
View Code

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
",ask(1,n,l,r));
25     }
26     return 0;
27 }
View Code

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 }
30

其实一定快要想到正解了,但是没有往深处想:考虑操作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 }
AC

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
",ksm(m,n)%moder);return 0;}
26     if(k==n) {printf("%lld
",ksm(m,(n+1)>>1)%moder);return 0;}
27     if(k&1) printf("%lld
",ksm(m,2));
28     else printf("%lld
",m);
29     return 0;
30 }
View Code

牛客某比赛 小可爱序列

题目大意:

您需要维护一个长度为n的序列,小可爱只用了以下两种操作:

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
",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
",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("
");
52     }
53     for(int i=1;i<=n;i++)
54         x=q2.front(),q2.pop_front(),printf("%d ",x); 
55     return 0;
56 }
View Code

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
",tot);
28     return 0;
29 } 
View Code

正确做法:以失误为轴

 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 } 
View Code

 以及四舍五入(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
",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
",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测试,也出了一道类似排列的题,我们同样能用状压搞到不错的分数。原题是这样的

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9822775.html