你是能看到第一题的 friends 呢。 ——hjaios
众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。算法
网址压缩是现实世界的一个重要问题,随着网址数量的日益增加,长网址的存储已经给现现在的网络带来了巨大的压力。为了解决这个问题,网址压缩已经成为了一件迫在眉睫的事情(以上都是我瞎编的)。网络
更具体来讲,网址压缩的目标是但愿将较长的网址变为更短的网址,例如咱们能够将 https://www.baidu.com 压缩为 http://sb.cn/。为了更加形式化咱们的问题,测试
咱们如今的任务是给定N个只包含小写字母的字符串,你须要输出对这N个字符串进行压缩的结果。你可使用任意的压缩算法,但你须要保证知足以下的性质:spa
一、压缩的结果字符串仍然只有小写字母。code
二、压缩的结果不能是空串,且长度必需要比原来的字符串短。orm
三、相同的字符串压缩结果必须相同,不一样的字符串压缩结果必须不一样。blog
任意知足上述条件的压缩方法都是正确的,因此你的目标就是对给定的N个字符串进行压缩。数据保证有解排序
第一行一个整数𝑁。ip
接下来𝑁行每行一个字符串。
输出𝑁行每行一个字符串表明压缩的结果。
4
jzm
bilibili
hhhhh
jzm
ha
bilibil
hhhh
ha
对于100%的数据,𝑁 ≤ 1000,字符串长度≤ 50。大部分测试点直接随机造
数据,小部分测试点为构造数据。
全损压缩
先把全部的字符串读入,而后按照长度从小到大排序,而后一个一个压缩(a,b,c,...,z,aa,ab...这样子)
好像还有一个奇技淫巧,只须要排序以后把第一个字符删掉就能够过这道题
然鹅并无卡掉这种作法
#include<cstdio> #include<cstdlib> #include<cstring> #include<map> #include<string> #include<iostream> #include<algorithm> using namespace std; const int maxn=1010; map<string,string> ma; string z[maxn]; int n,l=1,res[233],y[maxn]; bool cmp(int a,int b) { return z[a]<z[b]; } int main() { cin >> n; for (int a=1;a<=n;a++) { cin>>z[a]; y[a]=a; } res[1]=1; sort(y+1,y+n+1,cmp); for (int a=1;a<=n;a++) { string now = z[y[a]]; if (ma.count(now)!=0) ; else { string cur = ""; for (int a=l;a>=1;a--) cur = cur + (char)(res[a]+'a'-1); ma[now] = cur; res[1]++; for (int a=1;a<=l;a++) if (res[a]>26) { res[a]=1; res[a+1]++; } if (res[l+1]) l++; } } for (int a=1;a<=n;a++) cout << ma[z[a]] << endl; return 0; }
你是能看到第二题的 friends 呢。
——aoao
众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。
Paradeus 是一个新兴的宗教组织,该组织包含了𝑁 − 1个 Nyto,以及一个Mercurows 总共𝑁我的组成。每一个 Nyto 都是被其余某我的传教而进入的 Paradeus,而 Mercurows 是宗教的创立者,也就是说 Mercurows 并无被任何人拉进组织。这张记录了每一个人是由谁拉进传销组织的记录被视为 Paradeus 的教义,一直被广为传颂。
然而,随着岁月的流逝,有不法分子开始对 Paradeus 的教义发动了攻击。不法分子在 Paradeus 的教义上添加了一条记录(𝑎, 𝑏),表明𝑏是由𝑎介绍入教的。这条记录的加入致使 Nyto 们发现教义已经不合法了。为了复兴教义,教徒们决定找到这条被不法分子加入的记录,并将其删除以恢复教义的荣光。更具体的说,如今给定𝑁对记录(𝑎𝑖, 𝑏𝑖)表明是𝑎𝑖将𝑏𝑖拉入教的。注意这𝑁条记录包含了被不法分子添加的那一条。如今咱们但愿你找到某一条记录,使得删掉这条记录以后剩下的𝑁 − 1条记录可以造成合法的教义。要注意的是,教义并没有标注谁是 Mercurows,因此任何人都有多是 Mercurows。
一棵外向树(全部边从根向外指),加一条边,找出这条边让他编号尽量大
第一行一个数𝑁表明人数。接下来𝑁行每行两个数𝑎𝑖, 𝑏𝑖表明一条记录。
一行一个数表明删掉第几条记录可以使得教义合法。若是有多种方案,输出编号最大的方案。数据保证有解。
3
1 2
1 3
2 3
3
对于40%的数据,𝑁 ≤ 1000。
对于另外20%的数据,可能成为 Mercurows 的人必定只有一个。
对于100%的数据,1 ≤ 𝑁 ≤ 10^5。
讨论
1.知道根节点是谁 看看有没有入度为0的点
横叉边和返祖边
其实是一种状况,就是有一个点入度为2
暴力删边两次看看连不连通(然而我直接贪心取了最大的,而后100-->80)
2.不能找到一个入度为0的点 -->必定有环
在这个环上删除任何一个边均可以
怎么找环?BFS,并查集,tarjan
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int maxn=100010; int n,en,head,tail,in[maxn],ex[maxn][2],q[maxn],f[maxn],pre[maxn][2]; bool use[maxn]; struct edge { int s,e,p; edge *next; }*v[maxn],ed[maxn]; void add_edge(int s,int e,int p) { en++; ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->p=p; in[e]++; ex[en][0]=s;ex[en][1]=e; } int get(int p) { if (f[p]==p) return p; else return get(f[p]); } void init() { en=0; memset(v,0,sizeof(v)); memset(in,0,sizeof(in)); } void read() { scanf("%d",&n); for (int a=1;a<=n;a++) { int s,e; scanf("%d%d",&s,&e); add_edge(s,e,a); } } bool work1(int p,int d) { memset(use,false,sizeof(use)); use[p]=true; head=tail=1; q[1]=p; for (;head<=tail;) { int p=q[head++]; for (edge *e=v[p];e;e=e->next) if (e->p!=d) { if (use[e->e]) return false; use[e->e]=true; pre[e->e][0]=p; pre[e->e][1]=e->p; q[++tail]=e->e; } } for (int a=1;a<=n;a++) if (!use[a]) return false; return true; } int work2(int p) { memset(use,false,sizeof(use)); use[p]=true; head=tail=1; q[1]=p; for (;head<=tail;) { int p=q[head++]; for (edge *e=v[p];e;e=e->next) { if (use[e->e]) return e->p; use[e->e]=true; q[++tail]=e->e; } } return -1; } int work() { for (int a=1;a<=n;a++) if (in[a]==2) { int p1=-1,p2=-1; for (int b=1;b<=n;b++) if (ex[b][1]==a) { if (p1==-1) p1=b; else p2=b; } if (p1>p2) swap(p1,p2); for (int b=1;b<=n;b++) if (in[b]==0) { if (work1(b,p2)) return p2; else return p1; } } for (int a=1;a<=n;a++) if (in[a]==0) return work2(a); for (int a=1;a<=n;a++) f[a]=a; for (int a=1;a<=n;a++) { int f1=get(ex[a][0]); int f2=get(ex[a][1]); if (f1==f2) { work1(ex[a][1],a); int p=ex[a][1],ans=a; while (p!=ex[a][1]) { ans=max(ans,pre[p][1]); p=pre[p][0]; } return ans; } f[f1]=f2; } return -1; } int main() { init(); read(); int p=work(); printf("%d\n",p); return 0; }
个人菜鸡80分代码(个人找环是在开始建了双向边,而后直接dfs,可是在上面的第一种状况直接取了max致使80pts)
#include<cstdio> #include<iostream> #include<cstdlib> #include<iomanip> #include<cmath> #include<cstring> #include<string> #include<algorithm> #include<queue> using namespace std; const int N=100005; int n; int head[N],ecnt,ver[N]; struct edge { int to,nxt; }edg[N<<1]; inline void add(int u,int v) { edg[++ecnt].to=v; edg[ecnt].nxt=head[u]; head[u]=ecnt; edg[ecnt+n].to=u; edg[ecnt+n].nxt=head[v]; head[v]=ecnt+n; } int in[N],out[N]; bool flag=0; void checkout() { for(int i=1;i<=n;i++) { if(out[i]>1) { int maxn=-1; for(int j=head[i];j;j=edg[j].nxt) { if(j<=n) maxn=max(maxn,j);//这里不对,应该删掉每一条边以后判断连通性 } printf("%d\n",maxn); flag=1; } } } bool huan[N]; bool vis[N]; int dfn[N]; int fa[N]; int cnt,sum; int ans[N]; void dfs(int now) { dfn[now]=++cnt; for(int i=head[now],v;i;i=edg[i].nxt) { v=edg[i].to; if(v==fa[now]) continue; if(dfn[v]) { //if(dfn[v]<dfn[now]) continue; ans[++sum]=v,huan[v]=1; for(;v!=now;v=fa[v]) ans[++sum]=fa[v],huan[fa[v]]=1; } else fa[v]=now,dfs(v); } } void checkhuan() { dfs(1); } int zhx=-1; void getans(int x) { for(int i=head[x];i;i=edg[i].nxt) { if(i<=n) { int v=edg[i].to; if(huan[v]==1&&vis[v]==0) { vis[v]=1; zhx=max(zhx,i); getans(v); } } } } int main() { // freopen("remove.in","r",stdin); // freopen("remove.out","w",stdout); scanf("%d",&n); for(int i=1,u,v;i<=n;i++) { scanf("%d%d",&u,&v); add(v,u); in[u]++; out[v]++; } checkout(); if(flag==1) return 0; checkhuan(); getans(ans[1]); for(int i=1;i<=sum;i++) printf("%d ",ans[i]); } /* 5 5 2 1 2 2 3 3 1 2 4 */
你是能看到第三题的 friends 呢。
——laekov
众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。
自古以来,递茶就是一种传统美德,而给大佬递茶更是普遍活跃于表情包的存在。如今为了模拟给大佬递茶的工做,Alice 和 Bob 开始了递茶操做。一开始Alice 和 Bob 都有一个杯子里面装了𝑁吨的茶。如今每次 Alice 会等几率地随机向垃圾桶里面倒入4𝐾, 3𝐾, 2𝐾或者𝐾吨的茶,而且若是 Alice 倒了𝑥吨的茶,Bob 就会向垃圾桶里面导入4𝐾−𝑥吨的茶。注意每次操做的时候 Alice 或者 Bob 的茶有可能不够多,这个时候就能倒多少到多少。如今问 Alice 在四种操做彻底等几率的状况下,Alice 先把本身的茶倒光的几率加上 Alice 和 Bob 同时把茶倒光的几率的一半是多少。注意,Alice 和 Bob 每轮倒茶都是同时开始同时结束的。
第一行两个整数𝑁,𝐾。
输出一行一个六位实数表明答案。
2 1
0.625000
对于30%的数据,1 ≤ 𝑁,𝐾 ≤ 100。
对于60%的数据,1 ≤ 𝑁,𝐾 ≤ 1000。
对于另外20%的数据,𝐾 = 1。
对于100%的数据,1 ≤ 𝑁,𝐾 ≤ 10^9。
首先,k其实没有区别
N=10,k=5和n=2,k=1是同样的
把读进来的n变成n/k(上取整),k变成1
给你一个数,问另一个数(要注意这种问题能够经过某种玄学的方式找规律)
打表???
1. 爆搜
能够加上记忆化 f[i][j]表示a里面有i,b里面有j的几率是多少
最后求f[n][n]
F[0][j]=1,f[i][0]=0,f[0][0]=0.5
转移 f[i][j]=0.25*(f[i-4][j]+f[i-3][j-1]+f[i-2][j-2]+f[i-1][j-3]) 70pts
复杂度n^2
打表:随着n的增加,几率在增加
还有:答案只要求保留六位小数
因此,当n>?时 只须要输出1.000000??????
什么玄学东西????
#include<cstdio> #include<cstdlib> #include<cstring> using namespace std; const int maxn=201; int n,k; double f[maxn][maxn]; bool g[maxn][maxn]; double dfs(int n,int m) { if (n<=0 && m<=0) return 0.5; if (n<=0) return 1.0; if (m<=0) return 0.0; if (g[n][m]) return f[n][m]; g[n][m]=true; for (int a=1;a<=4;a++) f[n][m]+=0.25*dfs(n-a,m-4+a); return f[n][m]; } double work(int n,int k) { if (n/k>=maxn) return 1.0; return dfs((n/k)+(n%k?1:0),(n/k)+(n%k?1:0)); } int main() { scanf("%d%d",&n,&k); printf("%.6lf\n",work(n,k)); return 0; }
给大佬递茶
【问题描述】
你是能看到第三题的 friends 呢。
——laekov
众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没
什么关系。
自古以来,递茶就是一种传统美德,而给大佬递茶更是普遍活跃于表情包的
存在。如今为了模拟给大佬递茶的工做,Alice 和 Bob 开始了递茶操做。一开始
Alice 和 Bob 都有一个杯子里面装了𝑁吨的茶。如今每次 Alice 会等几率地随机向
垃圾桶里面倒入4𝐾, 3𝐾, 2𝐾或者𝐾吨的茶,而且若是 Alice 倒了𝑥吨的茶,Bob 就
会向垃圾桶里面导入4𝐾− 𝑥吨的茶。注意每次操做的时候 Alice 或者 Bob 的茶有
可能不够多,这个时候就能倒多少到多少。如今问 Alice 在四种操做彻底等几率
的状况下,Alice 先把本身的茶倒光的几率加上 Alice 和 Bob 同时把茶倒光的概
率的一半是多少。注意,Alice 和 Bob 每轮倒茶都是同时开始同时结束的。