------------恢复内容开始------------
///一道简单dp,题意:让你求出给出数列中加起来可以整出 m的方案数
///一道简单dp,题意:让你求出给出数列中加起来可以整出 m的方案数 ///dp【i】【j】表示前i个中你挑选n个(1<=n<=i)余数为j的方案数 ///所以转移方程为 dp[i - 1][j]未有i时的方案数加上 dp[i - 1][(j + f - a[i] % f) % f]即将加上i(所以得减去a【i】)这样+a[i]才有j #include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #include<cstdlib> #include<cctype> #include<vector> #include<stack> #include<queue> using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a) memset(a, 0, sizeof(a)) typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int mod = 1e8; const int maxn = 1e3 + 5; inline ll read() { ll ans = 0; char ch = getchar(), last = ' '; while(!isdigit(ch)) {last = ch; ch = getchar();} while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} if(last == '-') ans = -ans; return ans; } inline void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); } int n, f, a[maxn << 1]; int dp[maxn << 1][maxn]; int main() { n = read(); f = read(); for(int i = 1; i <= n; ++i) a[i] = read(); dp[0][0] = 1; for(int i = 1; i <= n; ++i) for(int j = f - 1; j >= 0; --j){ dp[i][j] = dp[i - 1][j] + dp[i - 1][(j + f - a[i] % f) % f]; dp[i][j] %= mod; } write(dp[n][0] - 1); enter; return 0; }
Remove the Substring
:给出两个字符串,其中一个为另一个子字符串(这里有不同的定义子字符串),问我们可以最多可以删除第一个字符串多长长度,任然满足第二个为子字符串,删除的只能为一个连续区间的字符串
首先我们在第一个字符串里面可以确保有一个子字符串,那么我们怎样删除呢 第一种情况:删除这个子字符串前面的一部分 第二种情况:删除这个子字符串后面的一部分 第三章情况:删除这个子字符串中间的一部分 然后需要统计最左边最先出现这个子字符串和最右边最先出现这个子字符串 #include<bits/stdc++.h> using namespace std; const int mx=2e5+10; char s1[mx],s2[mx];//输入的两个字符串 int h1[mx],h2[mx];//分别统计最左边和最右边 int main() { scanf("%s%s",s1,s2); int l1=strlen(s1); int l2=strlen(s2); int i,j; for(i=j=0;i<l1&&j<l2;i++)//首先统计最左边什么时候出现完整的子字符串 if(s1[i]==s2[j]) h1[j++]=i; j=l2-1; for(i=l1-1;i>=0&&j>=0;i--)//再统计最后边 if(s1[i]==s2[j]) h2[j--]=i; int m1=max(h1[0],l1-1-h1[l2-1]);//因为统计了最左边,看看删除左右长度 int m2=max(h2[0],l1-1-h2[l2-1]);//这个统计了最右边,同理 int mm=max(m1,m2); for(int i=1;i<=l2-1;i++) mm=max(mm,h2[i]-h1[i-1]-1);//这里要注意后面为i-1 printf("%d ",mm); //因为i相等时,s2[i]这个字符有两次 return 0; }
------------恢复内容结束------------
洛谷p1685
题意是一张图,从西到东有多条线路,你可以游览多次,但是你每次必须走不同道路,只要有一条不同于上一次条,就认为不同,求出所有方案路线总花费
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; const int mod=10000; #define int long long int in[maxn],dist[maxn],cnt[maxn],tot,h[maxn*2],n,m,s,t0,t; queue<int>q; int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } struct node { int dis,to,nxt; }edge[2*maxn]; void add(int u,int v,int d) { edge[++tot].nxt=h[u]; edge[tot].to=v; edge[tot].dis=d; h[u]=tot; in[v]++; } void bfs( ) { q.push(s);cnt[s]=1; while(q.size()){ int x=q.front();q.pop(); for(int i=h[x];i;i=edge[i].nxt){ int v=edge[i].to; (dist[v]+=dist[x]+cnt[x]*edge[i].dis)%=mod; (cnt[v]+=cnt[x])%=mod; in[v]--; if(!in[v]) q.push(v); } } } signed main() { int u,v,d; n=read(),m=read(),s=read(),t=read(),t0=read(); for(int i=1;i<=m;i++){ u=read(),v=read(),d=read(); if(u!=v) add(u,v,d); } bfs(); printf("%lld ",(dist[t]%mod+(cnt[t]-1)*t0%mod)%mod); return 0; }
csl的训练
一道拓扑排序题
https://ac.nowcoder.com/acm/contest/551/G
a1+r1∗k+((a1+r1∗k)+r2∗k))+(((a1+r1∗k)+r2∗k))+r3∗k)+...=sum
要使k最大,那么a1肯定等于0,再将k提出来,就会变k*total<=s;
total就是每个节点的值,从题目要求得知,我们得从第一个人(入度为0的那一个开始)
而total等于每个节点的值,都由前面的点决定,故拓扑排序,所以我们用0连接起所有的点,
接下来就可以得出一个入度为0的点开始拓扑
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=6e5+10; const int mod=10000; ll in[maxn],dist[maxn],cnt[maxn],tot,h[maxn*2],n,m,s,t0,t; queue<ll>q; ll read(){ char c=getchar();ll x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } struct node { ll dis,to,nxt; }edge[2*maxn]; void add(ll u,ll v,ll d) { edge[++tot].nxt=h[u]; edge[tot].to=v; edge[tot].dis=d; h[u]=tot; in[v]++; } ll bfs(ll s) { ll ans=0; q.push(s); dist[s]=0; while(q.size()){ ll x=q.front();q.pop(); for(ll i=h[x];i;i=edge[i].nxt){ ll v=edge[i].to; ll r=edge[i].dis; dist[v]=max(dist[v],dist[x]+r); in[v]--; if(!in[v]) q.push(v); } } for(ll i=1;i<=n;i++) ans+=dist[i]; return ans; } int main() { ll u,v,d,ans; n=read(),m=read(),s=read(); for(ll i=1;i<=m;i++){ u=read(),v=read(),d=read(); add(u,v,d); } for(ll i=1;i<=n;i++){ add(0,i,0); } ans=bfs(0); if(ans==0) printf("-1 "); else if(ans>s) printf("0 "); else printf("%lld ",s/ans); return 0; }
codeforces1027D Number Of Permutations(容斥)
题目要求,给出二元数组【x,y】,排列这些二元数组,最多有多少种,x也不递增,y也不递增(包括相等)的方案。
算出不单调递增的有多少个非常困难,但是容斥来算出总排列再减掉x相等或递增方案却很简单。
1.先考虑问题的简化版本:如果是一元组x,并且没有相同的情况,怎么求?此时x的递增情况只有一种,其他情况全是非递增的,只需要n的阶乘-1就可以了。
2.再考虑:如果一元组x,有重复的数字,怎么办?此时x的递增情况,与其中的重复数组有关,比如1 2 2。第二个2和第三个2可以调换位置。此时的递增数目为所有重复数目的阶乘和的积。再用fac[n]减去重复部分就可以了。
那二元的话我们只需减掉x的单调递增以及y的单调递增然后因为可能出现x单调递增会和y单调递增重复部分,所以还得再加上这部分重复的
#include<bits/stdc++.h> #define fi first #define se second #define ll long long using namespace std; const int N=3e5+10; const int mod=998244353; int n; ll p[N]; ll gs[N],gs1[N]; void cl(){ p[0]=1; for(int i=1;i<=n;i++)p[i]=(p[i-1]*i)%mod; } pair<int,int>P[N]; bool cmp(pair<int,int>x,pair<int,int>y){ if(x.fi!=y.fi)return x.fi<y.fi; else return x.se<y.se; } int main() { scanf("%d",&n); cl(); ll ans=p[n]; for(int i=1,x,y;i<=n;i++){ scanf("%d%d",&x,&y);P[i].fi=x,P[i].se=y;gs[x]++,gs1[y]++; } ll ans1=1,ans2=1,ans3=1; for(int i=1;i<=n;i++)if(gs[i])ans1=(ans1*p[gs[i]])%mod; for(int i=1;i<=n;i++)if(gs1[i])ans3=(ans3*p[gs1[i]])%mod; sort(P+1,P+n+1,cmp); int flag=0; for(int i=1;i<n&&!flag;i++){ if(P[i].se>P[i+1].se)flag=1; } int t1=P[1].fi,t2=P[1].se;ll cnt=0; for(int i=1;i<=n;i++){ if(t1==P[i].fi&&t2==P[i].se)cnt++; else{ if(cnt>1)ans2=(ans2*p[cnt])%mod; t1=P[i].fi;t2=P[i].se; cnt=1; } } ans2=(ans2*p[cnt])%mod; if(flag)ans2=0; //cout<<ans<<" "<<ans1<<" "<<ans3<<" "<<ans2<<endl; cout<<(ans-ans1-ans3+ans2+2*mod)%mod<<endl; }
齐肯多夫定理
齐肯多夫(Zeckendorf)定理表示任何正整数都可以表示成若干个不连续的斐波那契数(不包括第一个斐波那契数)之和。这种和式称为齐肯多夫表述法。
death to binary
题意:给你一个两个字符串,一个字符串的值等于为1位置的斐波那契的和,比如1101001Fib = F0 + F3 + F5 + F6 = 1 + 5 + 13 + 21 = 40,一个值可能有多种不同的写法,需要改成没有相邻的1的写法, 写成加法的式子;
#include<iostream> #include<cstring> #include<cstdio> using namespace std; typedef long long ll; ll fn[50]={1,2,3,5,8}; void init(){ for(int i=5;i<50;i++) fn[i]=fn[i-1]+fn[i-2]; } void out(ll num,string& str){ int i; for(i=41;i>=0;i--){ if(num>=fn[i]){ break; } } for(int j=i;j>=0;j--){ if(num>=fn[j]){ str+='1'; num-=fn[j]; }else{ str+='0'; } } if(i<0) str+='0'; } int main() { init(); string str1,str2; while(cin>>str1>>str2){ int len1=str1.length(); int len2=str2.length(); ll num1=0,num2=0,num3; for(int i=0;i<len1;i++) if(str1[len1-1-i]=='1') num1+=fn[i]; for(int i=0;i<len2;i++) if(str2[len2-1-i]=='1') num2+=fn[i]; num3=num1+num2; string str3; str1.clear();str2.clear(); out(num1,str1); out(num2,str2); out(num3,str3); cout<<" ";for(int i=0;i<str3.length()-str1.length();i++) cout<<' '; cout<<str1<<endl; cout<<"+ ";for(int i=0;i<str3.length()-str2.length();i++) cout<<' '; cout<<str2<<endl; cout<<" ";for(int i=0;i<str3.length();i++) cout<<'-'; cout<<endl; cout<<" "; cout<<str3<<endl; cout<<endl; } return 0; }
crazy calender
题意:r*c方格中,每个格子有一定石子,每次移动每格任意数量石子,只能向下或者向右动一格,不能移动为败
思路:显然是Nim,到右下曼哈顿距离为偶数的不用管,因为先手动一下后手动一下最后移到右下后还是先手的回合;奇数移动一格必到偶数格,所以奇数的Nim一下。很简单的入门题。
#include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; const int maxn = 5e4 + 10; const int seed = 131; const ll MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; int main(){ int T, r, c, Case = 1; ll n, ans; scanf("%d", &T); while(T--){ ans = 0; scanf("%d%d", &r, &c); for(int i = 1; i <= r; i++){ for(int j = 1; j <= c; j++){ scanf("%lld", &n); int dis = r - i + c - j; if(dis & 1) ans ^= n; } } if(ans) printf("Case %d: win ", Case++); else printf("Case %d: lose ", Case++); } return 0; }
HDU1593一道简单但还算有意思的题
一日,话说0068与***泛舟湖上。忽见岸边出现他的一大敌人elnil。0068当然不想落入elnil的魔爪,于是他就得想办法逃脱。
这个湖是一个很规则的圆形,半径为R。此时0068正好在圆心位置。小船在湖中的速度为 V1,0068和elnil在岸上的速度都为V2。也就是说,如果0068在刚上岸的时候没被抓到,则他可逃脱。在任意时刻,0068和elnil都可以朝任何方向移动,但是0068不能一直呆上船上(会饿死的),elnil不能下水(他不会游泳)。假设0068和elnil都非常聪明,总能做对自己最有利的事情,而且两个人的体力都是无限的。
请问,0068最终能不能逃脱elnil的魔爪?
————
一开始直接简单判断原点的0068朝着与敌人相反的方向跑,但是其实是错的,我们知道角速度公式是w = v / r
也就是在原点开始的0068想做个圆周运动(也就是保持和敌人一条直径上,这样位移最长嘛),由于r是小于圆的半径R的,故我们一开始的角速度决定比敌人快,这样我们可以自己保持和A一样的角速度走,但是你的角速度随着r的增大在不断减少,所以我们的边界就是当可达最大角速度等于敌人时,也就是下图,我们就立即直接往对岸直线跑,所以我们可以求出r,你的直接跑的路径为R-r;
#include <iostream> #include <math.h> using namespace std; const double pi = acos(-1.0); int main() { double r,r1,v1,v2,t1,t2; while(cin >> r >> v1 >> v2) { r1 = r/v2*v1; t1 = (r-r1)/v1; t2 = pi*r/v2; if (t1 < t2) puts("Yes"); else puts("No"); } return 0; }
codeforces 1204 C Anna, Svyatoslav and Maps
就是你的v序列最短,然后前提是题目给出p路径必须是v的最短路路径
例如例1;1 2 3 4,你可以缩减为1 2 4,因为2到4就只有3这条,你不可能多出其他条路径出来使原来2到4缩短,所以你可以很随意地去掉他,但是你不能说直接缩成 1 4,因为这样子12 3 4不是1 4的最短路径,我可以通过更短的1 3 4来完成
#include<cstdio> #include<iostream> #include<algorithm> #include<string.h> #define pn 100100 #define inf 99999999 using namespace std; typedef long long ll; int map[200][200]; int n,m; int oi[1000005]; bool book[1000005]; void floyed() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(map[i][j]==0&&i!=j) map[i][j]=inf; for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) map[i][j]=min(map[i][j],map[i][k]+map[k][j]); // printf("%d= ",map[1][3]); } int main() { char ch; scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf(" %c",&ch),map[i][j]=ch-'0'; floyed(); int st=1,ed=3; scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&oi[i]); ll sum=0; while(ed<=m) { if(map[oi[st]][oi[ed]]==ed-st) { book[ed-1]=1; sum++; } else { st=ed-1; } ed++; } sum=m-sum; printf("%d ",sum); for(int i=1;i<=m;i++) if(!book[i]) printf("%d ",oi[i]); }