Day1 P1 Vigenere密码
直接根据规律模拟即可
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2000;
char S[maxn],T[maxn];
int calc(char c)
{
return (int(c)-65-(c>=97?32:0));
}
int main()
{
scanf("%s",S);
scanf("%s",T);
for (int i = 0 ; T[i]!='\0'; ++i)
{
for (int j = 0 ; j < 26; ++j)
{
if ((j+calc(S[i%strlen(S)]))%26 == calc(T[i]))
printf("%c",j+65+(T[i]>=97?32:0));
}
}
return 0;
}
Day1 P2 国王游戏
首先我们分析一下 如果i和j两个相邻那么i排在j前面的必要条件是 total * a[i] / b[j] < total * a[j] /b[i] 也就是说 a[i]*b[i] < a[j]*b[j]
这样就立刻确定了顺序(有序性在信息学竞赛中的应用)
接下来就是模拟出每个大臣得到的数,直接使用一下高精度计算即可。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define INF 1000000000
using namespace std;
const int maxn = 200000;
struct big{
int a[maxn],len;
big(){ memset(a,0,sizeof(a)); len = 1;};
string str() const
{
string res = "";
for (int i = 0 ;i<len;++i)res= char(a[i]+'0')+res;
return res;
}
big operator = (const char* num)
{
len = strlen(num);
for (int i = 0;i < len;++i) a[i]=num[len-i-1]-'0';
while (!a[len-1] && len>1) len--;
return *this;
}
big operator = (int num)
{
char s[maxn];
sprintf(s, "%d", num);
*this = s;
return *this;
}
big operator * (int b)
{
big c ;
c.len = len;
for (int i = 0 ; i <c.len ; ++i)
c.a[i] = a[i]*b;
for (int i = 0 ; i <c.len; ++i)
{
c.a[i+1]+=c.a[i]/10;
c.a[i]%=10;
}
while (c.a[c.len]>0)
{
c.a[c.len+1]=c.a[c.len]/10;
c.a[c.len]%=10;
c.len++;
}
return c;
}
big operator /(int b)
{
big c= *this;
int x =0;
for (int i = len-1; i >=0; i--)
{
int y = (x*10+c.a[i])%b;
c.a[i] = (x*10+c.a[i])/b;
x = y;
}
/*if (x>0)
{
int i =0;
do{
a[i]++;
if (a[i]>=10) a[i+1]++,a[i]-=10;else
break;
}while(true);
}*/
while (c.a[c.len-1]==0 && c.len>1) c.len--;
return c;
}
big operator +(const big & b)
{
big c;
c.len = 0 ;
for (int i =0, g=0; g|| i< max(len, b.len);++i)
{
int x = g;
if (i<len)x+=a[i];
if (i<b.len) x+=b.a[i];
c.a[c.len++] = x%10;
g=x/10;
}
return c;
}
big operator +=(const big &b)
{
*this = *this + b;
return *this;
}
big(int num) { *this = num;}
big(const char* num){ *this = num ;}
bool operator < (const big &b)const
{
if (len!=b.len) return len<b.len;
for (int i = len-1; i>=0; i--)
if (a[i] != b.a[i]) return a[i]<b.a[i];
return false;
}
void print()
{
for (int i = len-1 ; i>=0;i--)
printf("%d",a[i]);
printf("\n");
}
}ans,answer,t;
struct node
{
int x,y;
bool operator <(const node& a) const
{
return x*y < a.x*a.y;
}
}p[maxn];
istream& operator >> (istream& in, big &x)
{
string s;
in>>s;
x = s.c_str();
return in;
}
ostream& operator << (ostream& out, big &x)
{
out<< x.str();
return out;
}
int main()
{
int n ;
cin >> n ;
for (int i = 0; i <=n ;++i)
scanf("%d %d",&p[i].x,&p[i].y);
sort(p+1, p+n+1);
ans = p[0].x;
answer = 0;
for (int i = 1 ; i <=n; ++i )
{
t = ans / p[i].y;
if (answer<t) answer = t;
ans =ans * p[i].x;
//cout<<p[i].x<<" ";
//ans.print();
}
answer.print();
return 0;
}
Day1 P3 开车旅行
A和B交替轮流开车一个选择最近的一个选择第二近的前进。
首先A和B交替前进可以看做是A+B一个整体前进。然后最后一步特殊处理即可。
然后关于找最近的点和第二近的点,类似于找前驱和后继,C++可以中set实现,至于Pascal语言,可以考虑平衡术,但是难度太大,还是用二叉查找树。
然后利用路径上的倍增思想,f[i][j]表示i点前进2^j次步走到了多少,g[i][j]表示i节点前进2^j次步走到哪个节点。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;
const int maxn = 200000+10;
typedef long long LL;
int g[maxn][20+2],next1[maxn],next2[maxn];
LL len1[maxn],len2[maxn];
LL lenA,lenB;
int n,m,i,j;
struct road
{
LL A,B;
road operator +(const road &a)
{
road c;
c.A = A + a.A;
c.B = B + a.B;
return c;
}
}f[maxn][20+2];
struct Point
{
int id, h;
bool operator<(const Point &a) const
{
return h < a.h;
}
}p[maxn];
multiset<Point> S;
multiset<Point>::iterator it;
void updata(int i ,int h,int j,int h2)
{
if (!next1[i])
{
next1[i] = j;
len1[i]= abs(h-h2);
return ;
}
if ((abs(h-h2) == len1[i] && p[next1[i]].h > h2)
|| (abs(h-h2) < len1[i]))
{
next2[i] = next1[i];
len2[i] = len1[i];
next1[i] = j;
len1[i] = abs(h-h2);
return ;
}
if (!next2[i])
{
next2[i] = j;
len2[i]= abs(h-h2);
return ;
}
if ((abs(h-h2)== len2[i] && p[next1[i]].h > h2)
|| (abs(h-h2) < len2[i]))
{
next2[i]= j;
len2[i] = abs(h-h2);
}
}
void ask(int st,LL len)
{
for (int j = 20; j >=0; j--)
{
if (g[st][j]!=0 && f[st][j].A+f[st][j].B<=len)
{
len -= (f[st][j].A+f[st][j].B);
lenA+=f[st][j].A;
lenB+=f[st][j].B;
st = g[st][j];
}
}
if (next2[st] && len>=len2[st])
lenA +=len2[st];
}
int main()
{
cin>>n;
for (int i = 1; i <= n; ++i)
{
scanf("%d", &p[i].h);
p[i].id = i;
}
for (int i = n; i>=1; --i)
{
it = S.lower_bound(p[i]);
if (it != S.end())
updata(i , p[i].h, it->id, it->h);
if (it != S.end())
{
it++;
if (it != S.end())
updata(i , p[i].h, it->id, it->h);
it--;
}
if (it != S.begin())
{
it--;
updata(i , p[i].h, it->id, it->h);
}
if (it != S.begin())
{
it--;
updata(i , p[i].h, it->id, it->h);
}
S.insert(p[i]);
}
for (int i = 1; i <= n;++i)
{
//cout << next2[i]<<" "<<len2[i]<<endl;
g[i][0] = next1[next2[i]];
f[i][0].A = len2[i];
f[i][0].B = len1[next2[i]];
}
for (int i = 1; i <= 20; ++i)
for (int j = 1; j <= n;++j)
{
g[j][i] = g[g[j][i-1]][i-1];
/*f[j][i].A = f[j][i-1].A + f[g[j][i-1]][i-1].A;
f[j][i].B = f[j][i-1].B + f[g[j][i-1]][i-1].B;
if (f[2][0].A==3)
cout<<i <<" "<<j << endl;
*/
f[j][i]= f[j][i-1]+ f[g[j][i-1]][i-1];
}
LL x;
cin>>x;
LL ansA = (LL)round(10e9);
LL ansB = 0;
int Pos = 0;
for (int i = 1; i <= n;++i)
{
lenA = lenB = 0;
ask(i,x);
//cout << lenA<<" "<<lenB<<endl;
if (lenB != 0)
{
if (Pos ==0 || ansA*lenB > lenA * ansB)
{
Pos = i;
ansA = lenA;
ansB = lenB;
}
}
}
cout << Pos << endl;
cin>>m;
for (int i = 1; i <= m; ++i)
{
int st;
scanf("%d %d",&st,&x);
lenA = lenB = 0;
ask(st,x);
cout<<lenA<<" "<<lenB <<endl;
}
return 0;
}
Day2 P1
同余方程 详见前面文章的两种解法(除了扩展欧几里得和分块外,还可以用费马定理做)。
Day2 P2 借教室
首先本题就是整体减一个值,看看数组里有木有值被减成了负数,这个可以用线段树维护最小值即可,看数组中的最小值是否为负数。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int maxn = 1000000+10;
int a[maxn];
int n,m,s,t,need;
struct node
{
int Min[maxn*4];
int dec[maxn*4];
void build(int a[],int l,int r,int p)
{
dec[p]=0;
if (l==r)
{
Min[p] = a[l];
return ;
}
int mid = (l+r) / 2;
build(a,l,mid,p+p);
build(a,mid+1,r,p+p+1);
Min[p] = min(Min[p+p],Min[p+p+1]);
}
void update(int p )
{
Min[p] = min(Min[p+p]-dec[p+p],Min[p]);
Min[p] = min(Min[p+p+1]-dec[p+p+1],Min[p]);
}
void push(int p)
{
dec[p+p]+=dec[p];
dec[p+p+1]+=dec[p];
dec[p]=0;
}
void getDec(int l ,int r,int p, int a,int b,int need)
{
if (l==a && r==b)
{
Min[p]-= (need+dec[p]);
if (l!=r) dec[p+p]+=dec[p]+need,dec[p+p+1]+=dec[p]+need;
dec[p]=0;
return ;
}
push(p);
int mid = (l+r) / 2;
if (b <= mid ) getDec(l,mid,p+p,a,b,need); else
if (a > mid ) getDec(mid+1,r,p+p+1,a,b,need); else
{
getDec(l, mid ,p+p,a,mid,need);
getDec(mid+1,r,p+p+1,mid+1,b,need);
}
update(p);
}
}tree;
int main()
{
cin >> n >> m ;
for (int i = 1; i <= n ;++i ) scanf("%d",&a[i]);
tree.build(a,1,n,1);
for (int i = 1; i <= m ;++i)
{
scanf("%d %d %d",&need, &s,&t);
tree.getDec(1,n,1,s,t,need);
if (tree.Min[1] < 0 )
{
printf("%d\n%d\n",-1,i);
return 0;
}
}
cout << 0 << endl;
return 0;
}
Day2 P3
疫情控制
题目大意即所有军队拦截住1节点通往叶节点的道路。
首先答案最小,可以用二分转化为判定性问题。
第二继续利用倍增思想判断军队能否达到1节点。能则记录还有多少时间可以移动,不能则停留在最上面的节点上。
然后根据目前情况DFS出1的那几个子节点需要军队,然后就是军队与子节点的一一匹配。
这个匹配可以用贪心,首先而比边的权值排序从大到小操作,对于1的某个自己点,如果有军队从他走到了1,但是回不到自身了,则
让该军队控制这个节点,否则选出还有时间能够走到该节点的剩余时间最小的军队去控制这个节点(可以用一个set,方便查找)。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
#include <map>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 100000;
typedef long long LL;
multiset<int> S;
multiset<int>::iterator it;
int n,u,v,w,m,nownode;
LL f[maxn][20];
int y[maxn];
int g[maxn][20];
int sonTree[maxn];
int cnt[maxn];
int p[maxn];
int Min[maxn];
bool Leaf[maxn];
bool son[maxn];
struct Arm
{
int opt,time;
}A[maxn];
struct Graph
{
int node[maxn * 2], len[maxn * 2], next[maxn * 2], v[maxn * 2];
int en;
Graph(){
memset(v,0,sizeof(v));
memset(next,0, sizeof(next));
memset(len,0,sizeof(len));
en = 0;
}
void addEdge(int a,int b,int c)
{
en++; node[en] = b; len[en] = c; next[en] = v[a]; v[a] = en;
en++; node[en] = a; len[en] = c; next[en] = v[b]; v[b] = en;
}
}G;
void DFS(int x,int father)
{
sonTree[x] = nownode;
bool flag= true;
for (int j = G.v[x]; j; j = G.next[j])
{
if (G.node[j] != father)
{
//father[j] = x;
flag = false;
f[G.node[j]][0] = G.len[j];
g[G.node[j]][0] = x;
if (x==1) nownode = G.node[j];
DFS(G.node[j],x);
}
}
Leaf[x] = flag;
}
void find_leaf(int x, int father)
{
if (cnt[x]>0) return ;
if (Leaf[x])
{
son[nownode] = true;
return;
}
for (int j = G.v[x]; j ; j = G.next[j])
{
if (G.node[j] != father)
{
if (x==1) nownode = G.node[j];
find_leaf(G.node[j], x);
}
}
}
bool ok(LL value)
{
S.clear();
int arm = 0;
memset(cnt,0,sizeof(cnt));
memset(son,0,sizeof(son));
for (int i = 1; i <= m ;++i)
{
int len = value;
int Pos = p[i];
for (int j = 16; j>=0; --j)
{
if (f[Pos][j]<=len && g[Pos][j]!=0)
{
len-=f[Pos][j];
Pos =g[Pos][j];
}
}
if (Pos == 1)
{
A[arm].opt = p[i];
A[arm++].time = len;
} else
cnt[Pos]++;
}
find_leaf(1,0);
for (int i = 1;i <= n;++i) Min[i]= -1;
for (int i = 0; i < arm ; ++ i)
{
if (son[sonTree[A[i].opt]])
{
if (Min[sonTree[A[i].opt]] ==-1 ||
A[Min[sonTree[A[i].opt]]].time > A[i].time)
Min[sonTree[A[i].opt]] = i;
}
}
int tot = 0;
for (int i = 1; i <= n;++i)
{
if (son[i] && Min[i] != -1 && A[Min[i]].time<f[i][0])
{
A[Min[i]].time = -1;
}else
if (son[i]) y[tot++] = f[i][0];
}
sort(y,y+tot);
for (int i = 0; i < arm;++i)
if (A[i].time != -1)
S.insert(A[i].time);
for (int i = tot-1; i>=0; --i)
{
if (S.lower_bound(y[i]) == S.end()) return false;
it = S.lower_bound(y[i]);
S.erase(it);
}
return true;
}
int main()
{
cin>>n;
for (int i = 1;i < n;++ i)
{
scanf("%d %d %d",&u,&v,&w);
G.addEdge(u,v,w);
}
cin>>m ;
for (int i = 1;i <= m;++i)
{
scanf("%d", &p[i]);
// cnt[p[i]] ++;
}
DFS(1,0);
/*for (int i = 1;i <= n; ++i)
{
cout <<i<<" "<<f[i][0]<<" "<<g[i][0] << endl;
}*/
for (int i = 1; i <=16; ++i)
for (int j = 1; j <= n ;++j)
{
f[j][i] = f[j][i-1]+ f[g[j][i-1]][i-1];
g[j][i] = g[g[j][i-1]][i-1];
}
LL L = 0 , R = (LL)n * round(10e9);
int ans = -1;
while (L <= R)
{
LL mid = (L+R) /2;
if (ok(mid)) R = mid-1, ans = mid;
else L = mid+1;
}
cout << ans << endl;
return 0;
}